면접 준비 (1)

1. CS

1.1 프로세스와 스레드

프로세스와 스레드의 차이는 무엇인가요?
  • 프로세스는 실행 중인 프로그램의 독립적인 실행 단위입니다.
  • 각 프로세스는 자신만의 가상 메모리 공간과 운영체제 자원을 가집니다.
  • 스레드는 프로세스 내부의 실행 흐름 단위입니다.
  • 스레드는 각자 Stack과 레지스터 상태를 가집니다. (나머지 공유)
프로세스의 상태(Process State)와 전이 과정을 설명해 주세요.
  • New(생성) → Ready(준비) → Running(실행) → Terminated(종료)가 기본 흐름입니다.
  • Running 중 입출력 등 이벤트를 기다리게 되면 Waiting(대기)으로 전환되고, 완료되면 다시 Ready로 돌아갑니다.
  • Running에서 타임 슬라이스를 다 쓰면 Ready로 밀려나고(선점), 스케줄러가 Ready 중 하나를 골라 Running으로 올립니다.
멀티프로세스와 멀티스레드의 차이점
  • 멀티프로세스는 작업을 여러 프로세스로 나누어 수행합니다. 한 프로세스가 실패해도 다른 프로세스에 미치는 영향이 적고, 메모리 격리로 안정성이 높습니다. 반면 프로세스 생성과 컨텍스트 스위칭 비용이 크고, 프로세스 간 통신(IPC)이 복잡합니다.
  • 멀티스레드는 하나의 프로세스 안에서 여러 스레드가 자원을 공유합니다. 생성 및 전환 비용이 비교적 작고, 데이터 공유가 쉽습니다. 반면 하나의 스레드 오류가 전체 프로세스 장애로 이어질 수 있으며, Race Condition과 Deadlock 같은 동시성 문제를 관리해야 합니다.
프로세스 간 통신(IPC) 방식에는 어떤 것들이 있나요?
  • 프로세스는 메모리가 격리되어 있어 커널이 제공하는 IPC 수단이 필요합니다.
  • 파이프: 부모-자식처럼 관련 있는 프로세스 간 단방향 통신에 사용합니다.
  • 메시지 큐: 커널이 관리하는 큐에 메시지를 넣고 꺼내는 방식입니다.
  • 공유 메모리: 같은 메모리 영역을 매핑해 가장 빠르지만, 동기화를 직접 관리해야 합니다.
  • 소켓: 네트워크를 통해 서로 다른 호스트의 프로세스와도 통신할 수 있습니다. (HTTP 통신, DB 연결 등)
좀비 프로세스와 고아 프로세스는 무엇인가요?
  • 좀비 프로세스: 자식이 종료됐지만 부모가 종료 상태(exit status)를 아직 회수(wait)하지 않아 프로세스 테이블에 항목이 남아 있는 상태입니다. 많이 쌓이면 PID 고갈로 이어질 수 있습니다.
  • 고아 프로세스: 부모가 먼저 종료된 자식 프로세스입니다. init(PID 1) 프로세스가 새 부모가 되어 종료 시 정리를 맡습니다.
프로세스의 메모리 구조(Code, Data, Heap, Stack)를 설명해 주세요.
  • Code 영역: 실행할 프로그램의 기계어 코드가 저장됩니다. 일반적으로 읽기 전용입니다.
  • Data 영역: 전역 변수와 정적 변수 등이 저장됩니다. 초기화된 데이터와 초기화되지 않은 데이터를 구분하기도 합니다.
  • Heap 영역: 런타임에 동적으로 할당되는 객체와 데이터가 저장됩니다. Java 객체는 주로 Heap에 생성됩니다.
  • Stack 영역: 함수 호출 정보, 지역 변수, 매개변수, 반환 주소 등이 저장됩니다. 함수 호출이 끝나면 자동으로 정리됩니다.

1.2 운영체제와 커널

32bit와 64bit의 차이
  • CPU가 한 번에 처리하는 데이터 크기와 메모리 주소를 표현할 수 있는 범위
  • 32bit 주소는 이론상 2³² = 4GB 주소 공간을 표현
  • 64bit 주소는 이론상 2⁶⁴로 훨씬 큰 주소 공간을 표현
  • 실제 사용 가능 범위는 CPU·OS가 제한합니다.
유저 모드와 커널 모드, 시스템 콜을 설명해 주세요.
  • CPU는 보호를 위해 실행 권한을 유저 모드와 커널 모드로 나눕니다.
  • 유저 모드의 프로그램은 하드웨어나 다른 프로세스의 메모리에 직접 접근할 수 없고, 파일 읽기·네트워크 전송 같은 작업은 커널에 요청해야 합니다.
  • 이 요청 창구가 시스템 콜입니다. 호출하면 커널 모드로 전환되어 커널 코드가 대신 수행한 뒤 결과를 돌려줍니다.
  • 모드 전환에도 비용이 있으므로, 시스템 콜 횟수를 줄이는 것(버퍼링, 배치 처리 등)이 성능 최적화 포인트가 됩니다.
인터럽트(Interrupt)란 무엇인가요?
  • 입출력 완료, 타이머 만료 같은 사건이 발생했을 때 하드웨어나 소프트웨어가 CPU에 알리는 신호입니다.
  • CPU는 하던 일을 멈추고 해당 인터럽트 핸들러를 실행한 뒤 원래 작업으로 복귀합니다.
  • 폴링처럼 CPU가 계속 상태를 확인하지 않아도 되므로 효율적입니다. 선점형 스케줄링도 타이머 인터럽트로 구현됩니다.
컨텍스트 스위칭이란 무엇이며, 비용이 발생하는 이유는 무엇인가요?
  • 컨텍스트 스위칭은 CPU가 실행 중인 프로세스 또는 스레드를 바꾸는 과정입니다. 이때 기존 작업의 레지스터, 프로그램 카운터, 스택 포인터 등의 상태를 저장하고 다음 작업의 상태를 복원합니다.
  • 비용이 발생하는 이유는 상태 저장·복원뿐 아니라 CPU 캐시, TLB 같은 하드웨어 캐시의 효율이 떨어질 수 있기 때문입니다. 특히 프로세스 전환은 주소 공간도 바뀔 수 있어 스레드 전환보다 일반적으로 더 무겁습니다.

1.3 CPU 스케줄링

CPU 스케줄링의 목적과 대표 알고리즘을 설명해 주세요.
  • 다음에 CPU를 사용할 프로세스를 선택하는 작업
  • 대표 알고리즘
    • FCFS: 먼저 도착한 작업을 먼저 수행 -> 단순하지만 긴 작업이 뒤 작업을 오래 막을 수 있음
    • SJF: 실행 시간이 짧은 작업을 먼저 수행 -> 평균 대기 시간은 줄일 수 있지만 실행 시간을 미리 알기 어렵고 긴 작업이 굶을 수 있습니다.
    • Round Robin: 정해진 시간 할당량만큼 번갈아 실행 -> 시분할 환경에 적합하지만, 할당량이 너무 짧으면 전환 비용이 커집니다.
    • Priority Scheduling: 우선순위가 높은 작업을 먼저 수행 -> 낮은 우선순위 작업의 기아 상태를 막기 위해 Aging을 사용할 수 있음
선점형 스케줄링과 비선점형 스케줄링의 차이는 무엇인가요?
  • 선점형 스케줄링
    • 운영체제가 실행 중인 프로세스의 CPU를 회수하여 다른 프로세스에 할당
    • 응답성이 좋고 우선순위 처리에 유리하지만, 컨텍스트 스위칭 비용과 동기화 복잡성이 증가
    • Round Robin이 대표적
  • 비선점형 스케줄링
    • 프로세스가 종료되거나 입출력 대기 상태가 될 때까지 CPU를 계속 사용
    • 구현은 단순하지만 긴 작업이 CPU를 점유하면 다른 작업의 응답성이 나빠짐
    • FCFS가 대표적
동기·비동기, 블로킹·논블로킹의 차이는 무엇인가요?
  • 동기 호출한 쪽이 작업 완료와 결과를 직접 확인하며 다음 작업 수행
  • 비동기 호출한 쪽은 작업을 요청한 후 다른 일을 하고, 완료 결과는 콜백 등으로 추후 확인
  • 블로킹 작업이 완료될 때까지 호출 스레드가 대기합니다.
  • 논블로킹 호출이 즉시 반환되고, 호출 스레드는 다른 작업을 할 수 있습니다.
  • 정리하면 동기·비동기는 "완료를 누가 챙기는가", 블로킹·논블로킹은 "호출이 제어권을 바로 돌려주는가"의 관점입니다.
CPU bound 작업과 I/O bound 작업의 차이는 무엇인가요?
  • CPU bound: 연산 자체가 병목인 작업입니다. (이미지 인코딩, 암호화 등) 코어 수 이상으로 스레드를 늘려도 효과가 없습니다.
  • I/O bound: 디스크·네트워크 응답 대기가 병목인 작업입니다. (DB 조회, 외부 API 호출 등) 대기 시간이 길므로 스레드를 늘리거나 비동기 I/O로 처리량을 높일 수 있습니다.
  • 웹 서버는 대부분 I/O bound이므로, 스레드 풀 크기나 비동기 처리 전략을 정할 때 이 구분이 기준이 됩니다.

1.4 동시성 제어

Race Condition은 무엇이며 어떻게 예방하나요?
  • 여러 실행 흐름이 공유 자원에 동시에 접근할 때, 실행 순서에 따라 결과가 달라지는 문제
  • 두 스레드가 동시에 count++를 수행하면 둘 다 같은 기존 값을 읽어 증가 결과가 한 번만 반영될 수 있습니다.
  • 예방 방법은 뮤텍스·세마포어·모니터 같은 동기화 도구로 임계 구역을 보호하거나, 원자적 연산을 사용하거나, 불변 객체 및 메시지 전달 방식으로 공유 상태 자체를 줄이는 것입니다.
스레드 안전(Thread Safety)이란 무엇이며 어떻게 보장하나요?
  • 여러 스레드가 동시에 호출해도 정확하게 동작하는 성질입니다.
  • 보장 방법
    • 상호 배제: 임계 구역을 lock(뮤텍스, synchronized 등)으로 보호합니다.
    • 원자적 연산: CAS 기반의 Atomic 클래스 등을 사용합니다.
    • 불변 객체: 상태를 바꿀 수 없으면 동기화 없이도 안전합니다.
    • 스레드 로컬: 스레드마다 독립적인 복사본(ThreadLocal)을 두어 공유 자체를 없앱니다.
뮤텍스와 세마포어의 차이는 무엇인가요?
  • 뮤텍스
    • 뮤텍스는 하나의 공유 자원에 대해 한 실행 흐름만 접근하게 하는 잠금 장치
    • 보통 잠근 스레드가 직접 해제해야 하며, 소유권 개념 존재
  • 세마포어
    • 세마포어는 허용 가능한 동시 접근 수를 카운터로 관리합니다.
    • 값이 1인 이진 세마포어는 상호 배제에 사용할 수 있고, 값이 여러 개인 카운팅 세마포어는 DB 커넥션 풀처럼 제한된 자원 개수를 관리할 때 적합
교착 상태(Deadlock)의 발생 조건 4가지는 무엇인가요?
  • 상호 배제: 자원을 한 번에 하나의 실행 흐름만 사용할 수 있습니다.
  • 점유 대기: 자원을 가진 상태에서 다른 자원을 기다립니다.
  • 비선점: 이미 할당된 자원을 강제로 빼앗을 수 없습니다.
  • 순환 대기: 실행 흐름들이 원형으로 서로가 가진 자원을 기다립니다.
교착 상태(Deadlock)에 대응하는 방법은 무엇이 있나요?
  • 예방: 발생 조건 4가지 중 하나를 깨는 방식입니다. 대표적으로 모든 실행 흐름이 자원을 같은 순서로 획득하게 해 순환 대기를 차단합니다.
  • 회피: 은행원 알고리즘처럼 자원을 할당하기 전에 안전한 상태인지 검사합니다.
  • 탐지 및 회복: 교착을 허용하되 주기적으로 탐지해 강제 종료나 자원 회수로 회복합니다. DB가 데드락을 감지해 한쪽 트랜잭션을 롤백하는 것이 대표적인 예입니다.
  • 실무에서는 락 타임아웃을 두어 무한 대기를 피하는 방식도 함께 사용합니다.
스핀락은 무엇이며, 뮤텍스보다 적합한 상황은 언제인가요?
  • 락을 얻을 때까지 잠들지 않고 반복해서 락 상태를 확인하는 방식
  • 대기 중에도 CPU를 사용
  • 따라서 락 보유 시간이 매우 짧고, 멀티코어 환경에서 락을 가진 실행 흐름이 곧 실행될 수 있을 때 적합합니다. 반대로 대기 시간이 길거나 단일 코어 환경에서는 CPU를 낭비하므로 뮤텍스처럼 대기 상태로 전환하는 방식이 더 적합합니다.

1.5 메모리 관리

가상 메모리란 무엇이며 왜 필요한가요?
  • 프로세스마다 실제 물리 메모리와 분리된 독립적인 주소 공간을 제공하는 메모리 관리 기법입니다.
  • 프로세스 간 메모리가 격리되어 서로 침범할 수 없고, 필요한 페이지만 적재(Demand Paging)하므로 물리 메모리보다 큰 프로그램도 실행할 수 있습니다.
  • 가상 주소 → 물리 주소 변환은 페이지 테이블과 MMU가 담당하며, 변환 결과를 캐싱하는 TLB로 속도를 보완합니다.
  • 아직 RAM에 올라오지 않은 페이지에 접근하면 Page Fault가 발생하고, OS가 해당 페이지를 RAM에 올린 뒤 실행을 계속합니다.
페이징과 세그멘테이션의 차이는 무엇인가요?
  • 페이징
    • 메모리를 고정 크기의 페이지와 프레임으로 나누어 관리
    • 외부 단편화를 줄일 수 있지만, 마지막 페이지에서 내부 단편화가 발생 가능
  • 세그멘테이션
    • 코드, 데이터, 스택처럼 논리적 의미를 가진 가변 크기 단위로 메모리를 관리
    • 프로그램 구조를 반영하기 쉽지만, 크기가 가변적이므로 외부 단편화가 발생할 수 있음
페이지 폴트는 무엇이며 발생하면 어떤 일이 일어나나요?
  • 프로세스가 접근하려는 가상 메모리 페이지가 현재 물리 메모리에 없을 때 발생하는 예외
  • 운영체제는 해당 페이지가 디스크의 스왑 영역 또는 실행 파일 어디에 있는지 확인하고, 빈 프레임이 없으면 교체할 페이지를 고릅니다. 이후 필요한 페이지를 메모리에 적재하고 페이지 테이블을 갱신한 뒤, 중단됐던 명령어를 다시 실행합니다.
  • 페이지 폴트가 지나치게 많으면 디스크 입출력에 대부분의 시간이 쓰이는 스래싱(Thrashing) 발생
페이지 교체 알고리즘에는 어떤 것들이 있나요?
  • 빈 프레임이 없을 때 어떤 페이지를 내보낼지 결정하는 알고리즘입니다.
  • FIFO: 가장 먼저 들어온 페이지를 교체합니다. 단순하지만 자주 쓰는 페이지도 내보낼 수 있습니다.
  • LRU: 가장 오랫동안 사용되지 않은 페이지를 교체합니다. 시간 지역성을 활용해 성능이 좋아 널리 쓰입니다. (Redis의 캐시 eviction 정책 등에도 같은 아이디어가 사용됩니다)
  • LFU: 사용 빈도가 가장 낮은 페이지를 교체합니다.
  • 실제 OS는 LRU를 근사한 Clock(Second Chance) 알고리즘 등을 사용합니다.
캐시 메모리와 지역성(Locality)의 관계를 설명해 주세요.
  • CPU와 주기억장치 간 속도 차이를 줄이기 위해 자주 사용할 데이터를 더 빠른 메모리에 저장하는 구조
  • 캐시가 효과적인 이유는 프로그램이 특정 데이터와 명령어를 반복하거나, 서로 가까운 주소를 순차적으로 접근하는 지역성 특성을 보이기 때문입니다. 지역성이 높을수록 캐시 적중률이 높아지고 성능이 좋아집니다.
캐시의 시간 지역성과 공간 지역성은 무엇인가요?
  • 시간 지역성
    • 최근에 접근한 데이터나 명령어를 가까운 미래에 다시 접근할 가능성이 높은 특성
    • 반복문에서 같은 변수에 반복적으로 접근하는 경우가 예입니다.
  • 공간 지역성
    • 한 메모리 주소에 접근하면 그 주변 주소도 곧 접근할 가능성이 높은 특성
    • 배열을 순차적으로 탐색하는 경우가 대표적입니다.

2. Network

2.1 네트워크 기초

인터넷에서 구글을 쳤을 때 어떤 일이 발생하나요?
  • URL 파싱
  • DNS 조회: 도메인 → IP 변환
  • 서버와 TCP 연결: 3-way handshake
  • HTTPS인 경우 TLS handshake
  • HTTP request → 서버 응답 → 브라우저 렌더링
OSI 7계층과 TCP/IP 4계층을 설명해 주세요.
  • OSI는 통신을 7단계로 나눈 개념 모델이고, TCP/IP는 실제로 쓰이는 4계층 구현체
  • TCP/IP의 응용 계층이 OSI의 응용·표현·세션을 묶은 것
  • 네트워크 접근 계층이 데이터링크·물리를 묶은 것
계층을 나누는 이유는 무엇인가요?
  • 관심사 분리 / 유지보수성
  • 특정 계층만 바꿔도 다른 계층에 영향없음
IP 주소와 서브넷은 무엇인가요?
  • IP 주소
    • 네트워크에서 장치를 식별하고, 데이터를 어느 장치로 보낼지 결정하기 위한 주소
    • 네트워크 주소와 호스트 주소로 구성
  • 서브넷
    • 하나의 큰 네트워크를 더 작은 네트워크 단위로 나눈 것
    • 서브넷 마스크를 통해 서브넷 구분
ARP는 무엇이며 왜 필요한가요?
  • 같은 네트워크(LAN) 상에서 IP 주소를 MAC 주소로 변환해주는 프로토콜
  • 실제로 데이터를 전달하려면 물리 계층에서는 MAC 주소가 필요하기 때문에 이 둘을 연결해주는 역할

2.2 TCP와 UDP

TCP와 UDP의 차이 및 각각 적합한 사용 사례는 무엇인가요?
  • TCP : 연결지향/신뢰성보장/HTTP통신이나 파일통신 등
  • UDP : 비연결지향/속도우선/스트리밍이나 영상, DNS 등
TCP의 3-way handshake 과정을 설명해 주세요.
  • 왜 3-way 인가?
    • TCP는 데이터를 주고받기 전에 연결을 먼저 수립하는데
    • 이 과정에서 세 번의 메시지 교환이 일어나서 3-way handshake
  • 3-way handshake 과정
    1. 클라이언트가 서버에게 연결을 요청하는 SYN을 보내면서 자신의 초기 시퀀스 번호를 알림
    2. 서버는 이를 받았다는 ACK와 함께, 자기 쪽에서도 연결하겠다는 SYN을 동시에 보냄
    3. 마지막으로 클라이언트가 서버의 시퀀스 번호를 확인했다는 ACK를 보내면 연결이 수립
TCP의 4-way handshake 과정을 설명해 주세요.
  • 4-way 인가?
    • TCP 연결 종료 시 양방향 종료 의사 필요
    • 이때 네 번의 메세지 교환이 일어나서 4-way handshake
  • 4-way handshake 과정
    1. Client → FIN → Server
    2. Server → ACK → Client
    3. Server → FIN → Client
    4. Client → ACK → Server → CLOSED
  • 2, 3 단계가 분리되어있는 이유
    • 서버 입장에서 클라이언트의 FIN을 받았다고 해서 자신도 즉시 종료할 수는 없음
    • 왜? 아직 전송할 데이터가 남아있을 수 있기 때문
    • 그래서, 우선 ACK로 "받았다"는 응답만 하고, 데이터 전송을 마친 뒤 별도로 FIN을 보냄
  • TIME_WAIT이 필요한 이유
    • 마지막 ACK가 유실될 경우를 대비하기 위함
    • ACK가 유실되면 서버는 FIN을 재전송
    • 이때 클라이언트가 이미 종료된 상태라면 재전송된 FIN에 응답할 수 없음 → 서버 정상종료불가
TCP의 흐름 제어와 혼잡 제어의 차이는 무엇인가요?
  • 흐름 제어 (목적은 수신 버퍼 오버플로우 방지)
    • 수신자의 처리 속도에 맞춰 송신 속도를 조절하는 것
    • 수신자가 자신의 버퍼 여유 공간을 Window Size로 알려주면,
    • 송신자는 그 범위 내에서만 데이터를 보냄
  • 혼잡 제어 (목적은 네트워크 과부하 방지)
    • 네트워크 상태에 맞춰 송신 속도를 조절하는 것
    • 송신자가 패킷 손실이나 지연을 감지해서 네트워크가 혼잡하다고 판단되면,
    • 전송량(Congestion Window)을 줄임
TCP의 재전송(Retransmission)은 언제, 어떻게 이루어지나요?
  • 주요 재전송 상황
    • 데이터 보낸 뒤 일정시간 안에 ACK가 도착하지 않으면 재전송
    • 이전 받은 데이터에 대한 중복 ACK 3회 수신 시
TCP Sequence Number의 역할은 무엇인가요?
  • 데이터의 바이트 단위 순서를 식별하는 번호
  • 수신 측 이것을 통해 순서가 뒤섞인 패킷을 재조립하고 유실 및 중복을 감지

2.3 HTTP

HTTP는 어떤 특징을 가진 프로토콜인가요?
  • Request-Response 구조
    • 클라이언트가 요청(Request)을 보내면 서버가 응답(Response)을 반환하는 구조
  • Stateless (무상태성)
    • 매 요청은 독립적이며, 서버는 이전 요청에 대한 정보를 기억 X
    • 로그인 상태 유지 같은 기능을 위해 쿠키(Cookie)나 세션(Session)을 별도로 사용
  • Connectionless (비연결성)
    • 매번 TCP 연결을 새로 맺는 비효율을 줄이기 위해,
    • HTTP/1.1부터는 Keep-Alive를 기본으로 사용해 연결을 재사용
HTTP 구성
  • Start Line
    • 요청 / 응답
    • 예: GET /users HTTP/1.1, HTTP/1.1 200 OK
  • Header
    • 요청 / 응답에 대한 메타데이터
  • Body
    • 실제 전송 데이터
    • JSON, HTML, 등
    • 물론 GET 본문이 없는 요청/응답도 존재
HTTP Method의 종류와 특징은 무엇인가요?
  • GET : 리소스 조회. 서버 상태를 변경하지 않아야 함.
  • POST : 리소스 생성·처리 요청. 같은 요청을 반복하면 결과가 달라질 수 있음.
  • PUT : 리소스 전체 수정 또는 생성. 멱등적.
  • PATCH : 리소스 일부 수정.
  • DELETE : 리소스 삭제. 멱등적.
  • HEAD : GET과 같지만 본문 없이 헤더만 반환.
  • OPTIONS : 서버가 지원하는 메서드·CORS 정책 등을 확인.
HTTP Status Code는 어떻게 분류되나요?
  • 1xx Informational: 요청 처리 중 정보 전달
    • 예: 100 Continue
  • 2xx Success: 요청 성공
    • 예: 200 OK, 201 Created, 204 No Content
  • 3xx Redirection: 다른 위치로 이동 필요
    • 예: 301 Moved Permanently, 302 Found, 304 Not Modified
  • 4xx Client Error: 클라이언트 요청 오류
    • 예: 400 Bad Request, 401 Unauthorized, 403 Forbidden, 404 Not Found
  • 5xx Server Error: 서버 처리 오류
    • 예: 500 Internal Server Error, 502 Bad Gateway, 503 Service Unavailable
멱등성과 안전한 메서드란 무엇인가요?
  • 멱등성 여러 번 요청해도 결과 상태가 같은 것
    • 같은 요청을 여러 번 보내도 서버의 최종 상태가 같은 성질
    • 예: PUT, DELETE
  • 안전한 메서드 서버 상태를 바꾸지 않는 것
    • 서버의 상태를 변경하지 않는 메서드
    • 예: GET, HEAD, OPTIONS
  • 참고로 GET은 안전하면서 멱등적이고, DELETE는 멱등적이지만 안전하지 않음
HTTP/1.1, HTTP/2, HTTP/3의 차이는 무엇인가요?
  • HTTP/1.1 Keep-Alive 도입, 하지만 HOL(Head-of-Line) Blocking 문제 존재
  • HTTP/2 멀티플렉싱으로 하나의 연결에서 여러 요청을 동시 처리, HOL Blocking 개선
  • HTTP/3 TCP 대신 QUIC(UDP 기반)를 사용해 연결 설정 속도와 패킷 손실 대응력 개선

2.4 HTTPS와 암호화

HTTPS는 어떻게 보안을 제공하나요?
  • HTTPS는 HTTP 통신에 암호화를 적용한 프로토콜
  • HTTP는 데이터를 평문으로 주고받기 때문에 중간에서 도청(스니핑)이나 변조가 가능
  • 이를 방지하기 위해 HTTP와 TCP 사이에 SSL/TLS 계층을 추가한 것이 HTTPS
  • 즉, HTTP+TLS = HTTPS
대칭키 암호화와 비대칭키 암호화의 차이는 무엇인가요?
  • 대칭키
    • 개요
      • 하나의 키로 암호화와 복호화를 모두 수행하는 방식입니다.
      • 송신자와 수신자가 같은 키를 공유하고 있어야 합니다.
    • 동작 방식
      • 평문을 키 A로 암호화 → 암호문
      • 암호문을 동일한 키 A로 복호화 → 평문
    • 장점
      • 연산이 단순해서 속도가 빠름
    • 단점
      • 키를 상대방에게 어떻게 안전하게 전달할지가 문제
      • 키가 전송 중 노출되면 누구나 암호문을 복호화할 수 있음 (키 배송 문제)
    • 대표 알고리즘
      • AES, DES, 3DES
  • 비대칭키
    • 개요
      • 한 쌍의 키(공개키, 개인키)를 사용하는 방식
      • 두 키는 수학적으로 연관되어 있지만, 하나로 다른 하나를 유추할 수 없음
      • 공개키(Public Key): 누구에게나 공개해도 되는 키
      • 개인키(Private Key): 소유자만 가지고 있는 절대 공개되지 않는 키
    • 동작 방식 (암호화 목적)
      • 데이터를 공개키로 암호화하면 → 그에 대응하는 개인키로만 복호화 가능
      • 예: 클라이언트가 서버의 공개키로 데이터를 암호화해서 보내면, 서버만 자신의 개인키로 열어볼 수 있음
    • 동작 방식 (서명 목적)
      • 데이터를 개인키로 서명하면 → 그에 대응하는 공개키로 검증 가능
      • CA가 개인키로 인증서에 서명하면, 누구나 CA의 공개키로 그 서명이 진짜인지 확인 가능
    • 장점
      • 키를 사전에 공유할 필요가 없어서 키 배송 문제 해결, 서명을 통한 인증 기능도 제공
    • 단점
      • 연산이 복잡해서 속도가 느림 (대칭키 대비 수백~수천 배)
    • 대표 알고리즘
      • RSA, ECC, ECDSA
TLS는 무엇이며, handshake 과정은 어떻게 진행되나요?
  • 네트워크 통신에 암호화, 인증, 무결성을 제공하는 프로토콜
  • 보안 요소
    • 기밀성: 대칭키 암호화
    • 무결성: MAC, 해시 기반 검증
    • 인증: CA가 발급한 인증서 기반
  • TLS handshake
    1. Client Hello - 클라이언트가 지원하는 TLS 버전, 암호 스위트(Cipher Suite) 목록 전송
    2. Server Hello - 서버가 사용할 버전과 암호 스위트 선택, 자신의 인증서(공개키 포함) 전달
    3. 인증서 검증 - 클라이언트가 CA를 통해 인증서 신뢰성 확인
    4. 키 교환 - 비대칭키 방식으로 대칭키(세션키)를 안전하게 교환
    5. 암호화 통신 시작 - 이후 데이터는 세션키(대칭키)로 암호화하여 주고받음
  • 버전별 특징
    • SSL 2.0/3.0 - 취약점으로 폐기됨
    • TLS 1.0/1.1 - 구식, 보안 취약점으로 대부분 지원 종료
    • TLS 1.2 - 오랫동안 표준으로 사용, 여전히 널리 쓰임
    • TLS 1.3 - Handshake 과정을 단순화해서 RTT를 1회로 단축(1.2는 2-RTT)
인증서와 CA(Certificate Authority)의 역할은 무엇인가요?
  • 인증서(Certificate)
    • 서버의 공개키가 진짜 그 서버(도메인)의 것임을 증명하는 전자 문서
    • 인증서에는 다음과 같은 정보가 담김
      • 서버(도메인)의 공개키, 발급 대상(도메인명), 발급자(CA) 정보, 유효 기간, CA 전자서명
  • CA(Certificate Authority)
    • CA는 신뢰할 수 있는 제3의 인증 기관으로, 인증서를 발급하고 서명하는 역할
    • 동작 방식
      1. 서버 운영자가 CA에게 자신의 공개키와 도메인 정보를 제출해 인증서 발급을 요청
      2. CA는 해당 서버가 실제로 그 도메인의 소유자인지 검증
      3. 검증 후, CA는 자신의 개인키로 그 정보(서버 공개키 + 도메인 정보 등)에 전자서명을 하여 인증서를 발급
    • 클라이언트의 검증 과정
      • 클라이언트(브라우저)는 미리 내장된 CA의 공개키를 이용해 인증서 서명을 검증
      • 서명이 유효하면 이 공개키는 정말 이 도메인의 것이 맞다고 신뢰 OK

3. DATABASE

3.1. 관계형 데이터베이스

관계형 데이터베이스란 무엇인가요?
  • 데이터를 행(Row)과 열(Column)로 이루어진 테이블(관계) 형태로 저장하고, 테이블 간 관계로 데이터를 표현하는 데이터베이스입니다.
  • SQL로 데이터를 정의·조작하며, 스키마가 정해진 정형 데이터를 다루는 데 적합합니다.
  • 대표적으로 MySQL, PostgreSQL, Oracle이 있습니다.
Table, Row, Column은 각각 무엇인가요?
  • Table: 같은 구조를 가진 데이터의 집합(엔티티)입니다.
  • Row(레코드): 테이블에 저장된 한 건의 데이터입니다.
  • Column(속성): 데이터가 갖는 각 항목(필드)입니다.
Primary Key의 역할과 조건은 무엇인가요?
  • 역할: 테이블의 각 행을 유일하게 식별합니다.
  • 조건: NULL을 허용하지 않고, 값이 유일해야 하며, 가급적 변하지 않는 값이어야 합니다.
  • 지정 시 자동으로 Unique 인덱스가 생성되어 조회 성능에도 도움이 됩니다.
Foreign Key란 무엇이며 왜 사용하나요?
  • 다른 테이블의 Primary Key(또는 Unique 컬럼)를 참조하는 컬럼입니다.
  • 테이블 간 관계를 표현하고, 존재하지 않는 값을 참조하지 못하도록 참조 무결성을 보장하기 위해 사용합니다.
Unique Constraint와 Primary Key의 차이는 무엇인가요?
  • 공통점: 둘 다 값의 유일성을 보장합니다.
  • 차이점: Primary Key는 테이블당 하나만 지정할 수 있고 NULL을 허용하지 않지만, Unique Constraint는 여러 개 지정할 수 있고 NULL을 허용합니다(NULL은 유일성 검사에서 제외).
자연키와 대리키의 차이는 무엇인가요?
  • 자연키(Natural Key): 주민번호, 이메일처럼 업무적으로 의미가 있는 속성을 기본키로 사용합니다.
  • 대리키(Surrogate Key): 업무 의미가 없는 임의의 값(Auto Increment, UUID 등)을 기본키로 사용합니다.
  • 대리키는 값이 바뀔 일이 없어 안전하고 조인 성능도 좋아 실무에서 더 선호됩니다.
정규화란 무엇이며 왜 필요한가요?
  • 데이터 중복을 줄이고 삽입·수정·삭제 이상(Anomaly)을 방지하기 위해 테이블을 목적에 맞게 분해하는 과정입니다.
  • 데이터 정합성을 높이고 저장 공간 낭비를 줄일 수 있습니다.
제1·2·3정규형은 무엇인가요?
  • 1NF: 모든 컬럼이 원자값을 가져야 합니다. (하나의 컬럼에 여러 값을 저장하는 반복 그룹 제거)
  • 2NF: 1NF를 만족하고, 기본키 일부에만 종속되는 부분 함수 종속을 제거합니다.
  • 3NF: 2NF를 만족하고, 기본키가 아닌 속성이 다른 비키 속성에 종속되는 이행적 종속을 제거합니다.
반정규화는 왜, 언제 사용하나요?
  • 조회 성능을 위해 의도적으로 데이터 중복을 허용하거나 테이블을 합치는 것입니다.
  • 정규화로 인한 잦은 조인이 성능 병목이 되는, 읽기 위주의 워크로드에서 주로 사용합니다.
  • 대신 데이터 정합성을 관리하는 비용(중복 데이터 동기화)이 늘어납니다.
1:1, 1:N, N:M 관계는 어떻게 모델링하나요?
  • 1:1: 한쪽 테이블에 상대 테이블의 PK를 FK(주로 Unique)로 둡니다.
  • 1:N: N쪽 테이블에 1쪽 테이블의 PK를 FK로 둡니다.
  • N:M: 두 테이블의 PK를 각각 FK로 갖는 중간(연결) 테이블을 둡니다.
N:M 관계에서 중간 테이블이 필요한 이유는 무엇인가요?
  • 관계형 DB의 컬럼은 하나의 값만 가질 수 있어 다대다 관계를 컬럼에 직접 표현할 수 없기 때문입니다.
  • 중간 테이블을 두면 N:M 관계를 두 개의 1:N 관계로 분해해 표현할 수 있습니다.
NULL은 무엇이며 = NULL 대신 IS NULL을 써야 하는 이유는 무엇인가요?
  • NULL은 값이 없거나 알 수 없는 상태를 의미하며, NULL과의 모든 비교 연산 결과는 참/거짓이 아닌 UNKNOWN이 됩니다.
  • = NULL은 항상 UNKNOWN으로 평가되어 결과가 매칭되지 않으므로, NULL 여부를 올바르게 판별하는 전용 연산자인 IS NULL을 사용해야 합니다.

3.2. SQL

SELECT의 기본 실행 순서는 어떻게 되나요?
  • FROM → JOIN → WHERE → GROUP BY → HAVING → SELECT → ORDER BY → LIMIT/OFFSET 순서로 실행됩니다.
  • 작성 순서(SELECT부터 시작)와 실제 실행 순서가 다르다는 점이 자주 나오는 포인트입니다.
WHEREHAVING의 차이는 무엇인가요?
  • WHERE: GROUP BY 이전에 개별 행을 필터링하며, 집계 함수를 사용할 수 없습니다.
  • HAVING: GROUP BY 이후 그룹(집계) 결과를 필터링하며, 집계 함수를 사용할 수 있습니다.
GROUP BY는 왜 사용하나요?
  • 특정 컬럼(들)을 기준으로 행을 그룹으로 묶어, SUM, COUNT, AVG 같은 집계 함수를 그룹 단위로 계산하기 위해 사용합니다.
COUNT(*)COUNT(column)의 차이는 무엇인가요?
  • COUNT(*): NULL을 포함한 전체 행의 개수를 셉니다.
  • COUNT(column): 해당 컬럼 값이 NULL이 아닌 행의 개수만 셉니다.
INNER JOINLEFT JOIN의 차이는 무엇인가요?
  • INNER JOIN: 두 테이블에서 조인 조건이 일치하는 행만 반환합니다.
  • LEFT JOIN: 왼쪽 테이블의 모든 행을 반환하고, 오른쪽 테이블에 일치하는 값이 없으면 NULL로 채웁니다.
CROSS JOIN은 무엇이며 언제 사용하나요?
  • 조인 조건 없이 두 테이블의 모든 행을 조합(카티션 곱)하는 조인입니다.
  • 날짜 × 상품처럼 가능한 모든 조합을 만들어야 할 때 사용합니다.
서브쿼리와 CTE의 차이는 무엇인가요?
  • 서브쿼리: 쿼리 안에 중첩된 쿼리로, 쿼리가 복잡해질수록 가독성이 떨어지고 재사용이 어렵습니다.
  • CTE(WITH절): 이름을 붙여 임시로 정의한 결과셋으로, 가독성이 좋고 같은 쿼리 내 여러 번 참조할 수 있으며 재귀 쿼리도 작성할 수 있습니다.
UNIONUNION ALL의 차이는 무엇인가요?
  • UNION: 두 결과를 합치면서 중복된 행을 제거합니다. (내부적으로 정렬/중복 제거 비용 발생)
  • UNION ALL: 중복 제거 없이 결과를 그대로 합쳐 더 빠릅니다.
Window Function은 무엇이며 GROUP BY와 어떤 차이가 있나요?
  • GROUP BY는 그룹별로 여러 행을 하나로 압축(집계)해서 반환합니다.
  • Window Function은 그룹 단위로 집계하면서도 원본 행을 그대로 유지한 채 각 행에 집계 결과를 함께 붙여줍니다. (예: RANK() OVER (...), SUM() OVER (...))
INSERT, UPDATE, DELETE 시 주의할 점은 무엇인가요?
  • WHERE절 없는 UPDATE/DELETE로 테이블 전체가 영향받지 않도록 주의해야 합니다.
  • 대량 데이터를 다룰 때는 트랜잭션 크기와 락 범위, 실행 시간을 고려해 배치로 나누는 것이 좋습니다.
  • 외래키 제약과 인덱스 갱신 비용도 함께 고려해야 합니다.
대량 데이터에서 페이징은 어떻게 구현하나요?
  • LIMITOFFSET을 이용해 원하는 페이지 구간만큼 조회하는 방식이 가장 기본적입니다.
  • ORDER BY와 함께 사용해 정렬 기준을 명확히 해야 페이지마다 일관된 결과를 얻을 수 있습니다.
OFFSET 페이징의 성능 문제는 무엇이며 Cursor/Keyset Pagination은 어떻게 해결하나요?
  • OFFSET은 건너뛸 행까지 모두 읽은 뒤 버리는 방식이라, 뒤 페이지로 갈수록 스캔량이 늘어나 느려집니다.
  • Cursor(Keyset) Pagination은 마지막으로 조회한 행의 정렬 기준 값(예: id)을 기준으로 WHERE id > 마지막id 조건으로 다음 페이지를 조회해, 인덱스를 활용하며 페이지 위치와 상관없이 일정한 성능을 냅니다.

3.3. 인덱스와 쿼리 최적화

인덱스란 무엇이며 왜 조회 성능이 빨라지나요?
  • 특정 컬럼 값으로 데이터 위치를 빠르게 찾을 수 있도록 별도로 정렬해 둔 자료구조(주로 B-Tree)입니다.
  • 인덱스가 있으면 Full Table Scan 대신 탐색 범위를 좁혀 조회할 수 있어 성능이 크게 향상됩니다.
B-Tree 인덱스의 구조와 특징은 무엇인가요?
  • 균형 트리 구조로 데이터가 정렬된 상태로 저장되고, 리프 노드가 실제 데이터(또는 데이터 위치)를 가리킵니다.
  • 등가 검색뿐 아니라 범위 검색(BETWEEN, >, <)과 정렬(ORDER BY)에도 유리해 대부분 RDBMS의 기본 인덱스로 사용됩니다.
Hash 인덱스와 B-Tree 인덱스의 차이는 무엇인가요?
  • Hash 인덱스: 해시값으로 O(1) 등가 비교만 빠르며, 범위 검색과 정렬에는 사용할 수 없습니다.
  • B-Tree 인덱스: 정렬되어 있어 등가·범위 검색·정렬을 모두 지원합니다.
Full Table Scan은 무엇인가요?
  • 인덱스를 사용하지 않고 테이블의 모든 행을 처음부터 끝까지 읽는 방식입니다.
  • 인덱스가 없거나, 옵티마이저가 조회 대상 행이 많아 인덱스를 쓰는 것보다 전체 스캔이 낫다고 판단할 때 발생합니다.
인덱스 선택도(Selectivity)란 무엇인가요?
  • 전체 데이터 중 특정 값이 얼마나 고유한지를 나타내는 비율(고유 값 수 / 전체 행 수)입니다.
  • 선택도가 높을수록(값이 다양할수록) 인덱스 효율이 좋고, 성별처럼 선택도가 낮은 컬럼은 인덱스 효과가 떨어집니다.
복합 인덱스란 무엇이며 컬럼 순서는 왜 중요한가요?
  • 여러 컬럼을 묶어서 만든 하나의 인덱스입니다.
  • B-Tree는 첫 번째 컬럼을 기준으로 먼저 정렬되므로, 자주 조건에 쓰이고 선택도가 높은 컬럼을 앞쪽에 배치해야 인덱스를 효율적으로 활용할 수 있습니다.
복합 인덱스의 선행 컬럼 조건이 중요한 이유는 무엇인가요?
  • 복합 인덱스는 왼쪽 접두사(Leftmost Prefix) 원칙에 따라 동작하기 때문에, 선행 컬럼 조건이 쿼리에 없으면 뒤쪽 컬럼만으로는 인덱스를 온전히 활용할 수 없습니다.
인덱스를 사용하지 못하거나 효율이 떨어지는 경우는 언제인가요?
  • 컬럼에 함수나 연산을 적용한 조건 (예: WHERE YEAR(date) = 2024)
  • LIKE '%검색어'처럼 앞쪽에 와일드카드가 붙는 검색
  • 선택도가 낮은 컬럼에 건 인덱스
  • 컬럼과 비교값의 데이터 타입이 달라 암묵적 형변환이 발생하는 경우
  • OR 조건이 섞여 있어 옵티마이저가 인덱스를 타지 못하는 경우
Covering Index란 무엇인가요?
  • 쿼리에 필요한 모든 컬럼이 인덱스 자체에 포함되어 있어, 실제 테이블 데이터에 접근하지 않고 인덱스만으로 결과를 반환할 수 있는 인덱스입니다.
인덱스가 많으면 항상 좋은가요?
  • 아닙니다. 조회 성능은 좋아지지만 INSERT/UPDATE/DELETE 시 관련된 모든 인덱스를 함께 갱신해야 해서 쓰기 성능이 떨어지고 저장 공간도 늘어납니다.
  • 자주 쓰이는 조회 조건 위주로 필요한 만큼만 만드는 것이 좋습니다.
인덱스가 INSERT, UPDATE, DELETE 성능에 미치는 영향은 무엇인가요?
  • 데이터가 변경될 때마다 해당 컬럼을 포함한 모든 인덱스의 B-Tree 구조도 함께 갱신해야 하므로, 인덱스가 많을수록 쓰기 연산의 비용이 커집니다.
실행 계획(Execution Plan)은 무엇이며 무엇을 확인해야 하나요?
  • 옵티마이저가 쿼리를 어떤 방식(인덱스 사용 여부, 조인 순서·방식 등)으로 실행할지 보여주는 계획입니다. (EXPLAIN 등으로 확인)
  • 인덱스 사용 여부, Full Scan 발생 여부, 예상 처리 행 수, 조인 순서와 방식, 정렬(filesort) 발생 여부 등을 확인해야 합니다.
느린 쿼리를 어떻게 분석하고 개선하나요?
  • 슬로우 쿼리 로그로 느린 쿼리를 식별한 뒤, EXPLAIN으로 실행 계획을 확인합니다.
  • 불필요한 Full Scan·정렬이 있는지 확인해 적절한 인덱스를 추가하고, 조건절·조인 조건을 최적화하며 SELECT * 같은 불필요한 컬럼 조회를 지양합니다.
  • 필요하면 쿼리 구조 자체를 재작성하거나 테이블/캐시 구조 개선까지 고려합니다.

3.4. 트랜잭션과 동시성

트랜잭션이란 무엇인가요?
  • 더 이상 나눌 수 없는 하나의 논리적 작업 단위로, 트랜잭션 안의 모든 연산은 전부 성공하거나 전부 실패해야 합니다.
ACID란 무엇인가요?
  • Atomicity(원자성): 트랜잭션의 연산은 전부 반영되거나 전부 반영되지 않습니다.
  • Consistency(일관성): 트랜잭션 전후로 데이터베이스는 제약조건 등을 만족하는 일관된 상태를 유지합니다.
  • Isolation(격리성): 동시에 실행되는 트랜잭션들이 서로 영향을 주지 않도록 격리합니다.
  • Durability(지속성): 커밋된 데이터는 장애가 발생해도 영구적으로 보존됩니다.
Commit과 Rollback의 차이는 무엇인가요?
  • Commit: 트랜잭션의 변경 사항을 데이터베이스에 영구적으로 반영합니다.
  • Rollback: 트랜잭션의 변경 사항을 취소하고 트랜잭션 시작 이전 상태로 되돌립니다.
트랜잭션이 필요한 사례를 설명해 주세요.
  • 계좌 이체처럼 출금과 입금이 함께 성공하거나 함께 실패해야 하는 작업
  • 주문 생성 시 재고 차감과 주문 데이터 저장처럼 여러 테이블 변경이 하나의 단위로 처리되어야 하는 작업
트랜잭션 격리 수준이 필요한 이유는 무엇인가요?
  • 여러 트랜잭션이 동시에 같은 데이터에 접근할 때 발생할 수 있는 Dirty Read, Non-repeatable Read, Phantom Read 등의 문제를 어느 수준까지 허용할지 정하기 위해 필요합니다.
  • 격리 수준이 엄격할수록 정합성은 높아지지만 동시성(성능)은 떨어지는 트레이드오프가 있습니다.
Read Uncommitted, Read Committed, Repeatable Read, Serializable의 차이는 무엇인가요?
  • Read Uncommitted: 커밋되지 않은 데이터도 읽을 수 있습니다. (Dirty Read 발생)
  • Read Committed: 커밋된 데이터만 읽습니다. (Dirty Read는 방지되지만 Non-repeatable Read는 발생 가능)
  • Repeatable Read: 트랜잭션 동안 같은 행을 반복 조회해도 항상 같은 결과를 보장합니다. (Non-repeatable Read 방지)
  • Serializable: 트랜잭션들을 순차적으로 실행한 것과 같은 효과를 보장하며, 가장 엄격하고 가장 느립니다.
Dirty Read, Non-repeatable Read, Phantom Read는 무엇인가요?
  • Dirty Read: 다른 트랜잭션이 아직 커밋하지 않은 데이터를 읽는 현상입니다.
  • Non-repeatable Read: 같은 트랜잭션 안에서 같은 행을 두 번 읽었는데, 그 사이 다른 트랜잭션의 커밋으로 값이 달라지는 현상입니다.
  • Phantom Read: 같은 조건으로 두 번 조회했는데, 그 사이 다른 트랜잭션이 삽입한 행이 새로 나타나는 현상입니다.
Lock이란 무엇이며 왜 필요한가요?
  • 여러 트랜잭션이 동시에 같은 데이터에 접근할 때 데이터 정합성을 지키기 위해 자원에 대한 접근을 제어하는 장치입니다.
Shared Lock과 Exclusive Lock의 차이는 무엇인가요?
  • Shared Lock(공유 락, S-Lock): 읽기 락으로, 여러 트랜잭션이 동시에 획득할 수 있지만 쓰기는 막습니다.
  • Exclusive Lock(배타 락, X-Lock): 쓰기 락으로, 한 트랜잭션만 획득할 수 있으며 다른 읽기·쓰기를 모두 막습니다.
낙관적 락과 비관적 락의 차이는 무엇인가요?
  • 비관적 락: 충돌이 자주 발생한다고 가정하고, 데이터를 읽을 때부터 락을 걸어 다른 트랜잭션의 접근을 막습니다.
  • 낙관적 락: 충돌이 드물다고 가정하고, 락 없이 작업하다가 커밋 시점에 값(버전)을 비교해 충돌 여부를 확인합니다.
낙관적 락은 보통 어떻게 구현하나요? (version 컬럼 등)
  • 테이블에 version 컬럼을 두고, 데이터를 읽을 때 함께 조회합니다.
  • 수정 시 UPDATE ... WHERE id = ? AND version = 읽은버전 조건으로 실행하고, 영향받은 행이 0건이면 다른 트랜잭션이 먼저 수정한 것으로 보고 충돌로 처리(예외 발생, 재시도)합니다.
MVCC란 무엇인가요?
  • Multi-Version Concurrency Control의 약자로, 데이터를 변경할 때 기존 값을 바로 덮어쓰지 않고 여러 버전을 유지해 읽기 작업이 락 경합 없이 자신의 시점에 맞는 스냅샷을 읽을 수 있게 하는 방식입니다.
  • InnoDB는 Undo Log를 이용해 MVCC를 구현합니다.
데드락은 어떻게 발생하나요?
  • 두 개 이상의 트랜잭션이 서로 상대방이 가진 락을 기다리며 순환 대기 상태에 빠질 때 발생합니다.
  • 예: 트랜잭션 A가 락1을 잡고 락2를 기다리는 동시에, 트랜잭션 B는 락2를 잡고 락1을 기다리는 경우입니다.
데드락을 예방하거나 대응하는 방법은 무엇인가요?
  • 예방: 여러 테이블·행에 접근할 때 항상 동일한 순서로 락을 획득하고, 트랜잭션을 짧게 유지하며 필요한 락만 최소 범위로 사용합니다.
  • 대응: DB가 데드락을 자동으로 감지해 한쪽 트랜잭션을 강제 롤백시키며, 애플리케이션은 이를 감지해 재시도 로직을 둡니다.
MySQL InnoDB의 기본 격리 수준은 무엇이며, 어떤 특성이 있나요?
  • 기본 격리 수준은 Repeatable Read입니다.
  • 일반적인 Repeatable Read와 달리, InnoDB는 갭 락(Gap Lock)과 넥스트 키 락(Next-Key Lock)을 함께 사용해 Phantom Read까지도 상당 부분 방지합니다.

3.5. 데이터베이스 설계와 운영

요구사항을 ERD로 설계하는 과정은 어떻게 되나요?
  • 요구사항에서 엔티티(명사)와 속성을 도출합니다.
  • 엔티티 간 관계와 카디널리티(1:1, 1:N, N:M)를 정의합니다.
  • 기본키·외래키를 설정하고 정규화를 적용합니다.
  • 필요하다면 성능을 위한 반정규화를 검토합니다.
회원·주문·상품 관계를 어떻게 설계하겠나요?
  • 회원(1) : 주문(N) 관계이므로 주문 테이블에 회원 FK를 둡니다.
  • 주문과 상품은 N:M 관계이므로, 주문상품(주문 아이템) 중간 테이블을 두어 주문 FK, 상품 FK와 함께 수량·주문 시점 가격 등을 저장합니다.
Soft Delete와 Hard Delete의 차이는 무엇인가요?
  • Hard Delete: 데이터베이스에서 행을 실제로 삭제합니다.
  • Soft Delete: 실제로 삭제하지 않고 deleted_at, is_deleted 같은 컬럼으로 삭제 여부만 표시합니다.
Soft Delete 사용 시 발생할 수 있는 문제는 무엇인가요?
  • 조회 쿼리마다 삭제 여부 조건을 빠뜨리지 않아야 하며, 누락 시 삭제된 데이터가 그대로 노출되는 버그가 생길 수 있습니다.
  • Unique 제약과 충돌할 수 있습니다. (삭제된 값과 동일한 값을 다시 등록하려 할 때)
  • 데이터가 계속 쌓여 저장 공간과 조회 성능에 영향을 줄 수 있습니다.
Audit Log는 무엇이며 왜 필요한가요?
  • 데이터의 생성·수정·삭제 등 변경 이력을 별도로 기록하는 로그입니다.
  • 문제 추적, 감사(컴플라이언스) 대응, 변경 원인 파악, 필요 시 복구를 위해 필요합니다.
데이터 이력 관리는 어떻게 설계할 수 있나요?
  • 변경이 발생할 때마다 새 버전의 행을 추가하는 별도의 이력 테이블(예: xxx_history)을 두는 방식이 일반적입니다.
  • valid_from/valid_to 같은 유효 기간 컬럼으로 시점별 상태를 관리하는 방식도 사용합니다.
변경 가능성이 큰 속성은 스키마에 어떻게 반영하나요?
  • 변경이 잦은 속성은 별도 테이블로 분리해 메인 테이블의 변경 빈도를 줄이거나, 이력을 남길 수 있는 구조로 설계합니다.
  • 스키마 자체가 자주 바뀔 수 있는 값은 key-value/JSON 컬럼으로 유연하게 저장하는 방법도 있지만, 조회 편의성과 정합성 검증이 약해지는 트레이드오프가 있습니다.
테이블을 분리해야 하는 기준은 무엇인가요?
  • 데이터의 성격(엔티티)이 서로 다르거나, 갱신 주기·빈도가 크게 다르거나, 접근 패턴(자주 조회 vs 거의 쓰이지 않는 대용량 컬럼)이 다를 때 분리를 고려합니다.
  • 정규화 원칙과 함께, 특정 컬럼이 너무 커서 조회 성능에 영향을 줄 때도 분리 대상이 됩니다.
데이터 정합성이란 무엇이며 어떻게 보장하나요?
  • 데이터가 모순 없이 일관되고 정확한 상태를 유지하는 것입니다.
  • DB 제약조건(PK, FK, Unique, Not Null, Check), 트랜잭션과 적절한 격리 수준, 애플리케이션 검증 로직을 함께 사용해 보장합니다.
DB 제약 조건과 애플리케이션 검증은 어떻게 역할을 나눠야 하나요?
  • DB 제약 조건: 데이터 정합성의 최후 방어선으로, 어떤 경로로 데이터가 들어오든 반드시 지켜야 하는 규칙(유일성, 참조 무결성 등)을 담당합니다.
  • 애플리케이션 검증: 사용자에게 친절한 에러 메시지 제공, 복잡한 비즈니스 규칙, UX 관점의 사전 검증을 담당합니다.
  • 두 계층에서 모두 검증하되, 최종적인 무결성 보장은 DB 제약 조건에 맡기는 것이 안전합니다.
N+1 문제란 무엇이며 어떻게 해결하나요?
  • 연관 데이터를 조회할 때, 목록 조회 쿼리 1번에 더해 각 행마다 연관 데이터를 조회하는 쿼리가 N번 추가로 발생하는 문제입니다. (예: 주문 목록 조회 후 각 주문마다 회원 정보를 따로 조회)
  • 해결: JOIN으로 한 번에 조회하거나, IN절로 연관 데이터를 일괄 조회(batch fetching)하고, ORM에서는 fetch join·즉시 로딩 설정 등을 사용합니다.
DB 커넥션 풀은 왜 필요한가요?
  • 매 요청마다 DB 커넥션을 새로 맺고 끊으면 TCP 연결·인증 등의 비용이 크기 때문에, 미리 커넥션을 여러 개 만들어 두고 재사용하기 위해 사용합니다.
  • 커넥션 생성 비용을 줄이고, DB에 몰리는 연결 수를 제한해 자원을 보호하며, 응답 속도를 높입니다.
읽기 트래픽과 쓰기 트래픽이 증가하면 어떤 확장 전략을 고려할 수 있나요?
  • 읽기 트래픽 증가: Read Replica(읽기 복제본) 구성, Redis 같은 캐시 도입, 인덱스·쿼리 최적화를 고려합니다.
  • 쓰기 트래픽 증가: 샤딩(Sharding)으로 데이터를 분산하거나, 쓰기 부하가 큰 테이블을 분리하고, 메시지 큐를 이용한 비동기 처리로 즉시 처리 대신 지연 처리하는 방법을 고려합니다.
  • 근본적으로는 Scale-up(서버 성능 증설)과 Scale-out(서버 대수 증설)을 함께 고려합니다.

3.6 캐시와 Redis

캐시는 어떤 데이터에 적용해야 효과적인가요?
  • 자주 조회되지만 자주 바뀌지 않는 데이터, 그리고 계산 비용이 큰 결과에 적합합니다.
  • 반대로 매번 최신값이 보장되어야 하거나 조회 빈도가 낮은 데이터는 이득은 적고 정합성 위험만 커집니다.
  • 즉 조회 빈도와 변경 빈도의 비율을 먼저 따져보는 것이 캐시 도입의 출발점입니다.
Redis는 어떤 특징을 가진 저장소인가요?
  • 인메모리 키-값 저장소로, 디스크가 아닌 메모리에서 동작해 매우 빠릅니다.
  • 명령을 싱글 스레드로 순차 처리하기 때문에 명령 하나하나의 원자성이 보장되어, 동시성 제어가 단순합니다.
  • String뿐 아니라 List, Hash, Set, Sorted Set 같은 자료구조를 제공해 랭킹 보드, 큐, 세션 저장 등 다양한 용도로 쓰입니다.
  • 다만 싱글 스레드이므로 전체 키를 훑는 명령 하나가 서버 전체를 멈춰 세울 수 있습니다. 운영 중에는 KEYS 대신 SCAN을 사용해야 합니다.
대표적인 캐시 전략에는 어떤 것들이 있나요?
  • Cache Aside: 애플리케이션이 캐시를 먼저 확인하고, 없으면 DB에서 읽어 캐시에 채웁니다. 가장 널리 쓰이는 방식입니다.
  • Write Through: 쓰기 시 캐시와 DB에 함께 기록합니다. 정합성이 좋지만 쓰기가 느려집니다.
  • Write Back: 캐시에만 먼저 쓰고 나중에 DB에 반영합니다. 쓰기가 빠르지만 반영 전에 장애가 나면 데이터가 유실됩니다.
  • 데이터가 변경될 때는 캐시를 갱신하기보다 삭제하는 편이 단순하고 안전합니다. 갱신은 동시 쓰기 상황에서 순서가 꼬일 수 있기 때문입니다.
캐시 무효화는 왜 어렵나요?
  • 원본은 바뀌었는데 캐시가 남아 있으면 사용자에게 오래된 값이 계속 제공됩니다.
  • 변경 시 캐시를 지우더라도, DB 커밋과 캐시 삭제 사이의 짧은 순간에 다른 요청이 옛 값을 다시 캐싱해 버릴 수 있습니다.
  • 그래서 무효화 로직과 별개로 TTL을 함께 두어, 문제가 생겨도 일정 시간 뒤에는 정합성이 회복되도록 안전장치를 마련합니다.
Cache Stampede는 무엇이며 어떻게 막나요?
  • 많은 요청이 몰리는 인기 키의 캐시가 동시에 만료되면, 그 순간 모든 요청이 한꺼번에 DB로 몰려 부하가 급증하는 현상입니다.
  • TTL에 약간의 임의 값을 더해 만료 시점을 분산시킵니다.
  • 락을 걸어 첫 번째 요청만 DB를 조회해 캐시를 채우고, 나머지는 그 결과를 기다리게 합니다.
  • 만료되기 전에 미리 갱신해 두는 방법도 있습니다.
Redis의 메모리가 가득 차면 어떻게 되나요?
  • maxmemory-policy 설정에 따라 동작이 달라집니다.
  • 캐시 용도라면 오래 사용되지 않은 키부터 지우는 LRU 계열 정책이 적합합니다.
  • 반대로 지워지면 안 되는 데이터를 담고 있다면 축출 대신 쓰기를 거부하는 정책을 쓰고, 메모리 사용량을 모니터링해야 합니다.
  • 캐시로 쓰면서 축출 정책을 잘못 설정하면, 지워지면 안 될 세션 같은 데이터가 조용히 사라져 장애로 이어질 수 있습니다.
Redis를 분산 락으로 사용할 때 주의할 점은 무엇인가요?
  • 락을 획득할 때 반드시 만료 시간을 함께 설정해야 합니다. 그렇지 않으면 락을 잡은 프로세스가 죽었을 때 아무도 그 락을 풀 수 없습니다.
  • 해제할 때는 그 락이 자신이 건 것인지 확인한 뒤 지워야 합니다. 확인 없이 삭제하면 이미 만료되어 다른 프로세스가 새로 획득한 락을 풀어버릴 수 있습니다.
  • 만료 시간이 실제 작업 시간보다 짧으면 두 프로세스가 동시에 임계 구역에 들어갈 수 있습니다.
  • 결제처럼 절대 중복되면 안 되는 작업이라면 Redis 락만 믿지 말고, DB의 유니크 제약처럼 확실한 안전장치를 함께 두는 것이 좋습니다.

4. 자료구조와 알고리즘

4.1 자료구조

배열(Array)과 연결 리스트(Linked List)의 차이는 무엇인가요?
  • 배열은 메모리에 데이터를 연속적으로 저장하고, 인덱스로 O(1)에 접근할 수 있습니다. 반면 중간 삽입·삭제는 뒤 원소를 옮겨야 해서 O(n)이 걸립니다.
  • 연결 리스트는 노드가 다음 노드의 주소를 참조하는 형태로 메모리에 흩어져 저장됩니다. 특정 위치 삽입·삭제는 포인터만 바꾸면 되어 O(1)이지만, 임의 접근(인덱스 접근)은 처음부터 순회해야 해서 O(n)입니다.
  • 배열은 캐시 지역성이 좋아 순차 접근 성능이 뛰어나고, 연결 리스트는 크기가 가변적이라 미리 크기를 알 필요가 없습니다.
스택(Stack)과 큐(Queue)의 차이, 그리고 실제 사용 사례는 무엇인가요?
  • 스택은 LIFO(Last In First Out) 구조로, 함수 호출 스택, 실행 취소(Undo), DFS, 괄호 짝 검사 등에 사용됩니다.
  • 큐는 FIFO(First In First Out) 구조로, BFS, 작업 큐, 프린터 대기열, 메시지 큐 등에 사용됩니다.
  • 데크(Deque)는 양쪽 끝에서 모두 삽입·삭제가 가능한 구조로, 스택과 큐를 모두 대체할 수 있어 슬라이딩 윈도우 문제 등에 자주 쓰입니다.
해시 테이블(Hash Table)은 어떻게 O(1)의 평균 시간 복잡도를 갖나요?
  • 키를 해시 함수로 변환해 나온 값을 배열의 인덱스로 사용해 데이터를 저장하기 때문에, 평균적으로 인덱스 계산만으로 바로 접근할 수 있습니다.
  • 다만 최악의 경우(해시 충돌이 몰릴 때)는 O(n)까지 저하될 수 있습니다.
해시 충돌(Collision)은 왜 발생하며, 어떻게 해결하나요?
  • 서로 다른 키가 같은 해시 값(인덱스)으로 매핑될 때 발생합니다. 해시 함수의 출력 범위가 유한한데 입력은 무한하기 때문에 비둘기집 원리상 피할 수 없습니다.
  • 체이닝(Chaining): 같은 인덱스에 연결 리스트나 트리로 여러 데이터를 이어 붙여 저장합니다.
  • 개방 주소법(Open Addressing): 충돌이 나면 다음 빈 슬롯을 탐사(선형·이차 탐사, 이중 해싱)해서 저장합니다.
  • 해시 함수를 고르게 분포시키고, 로드 팩터(load factor)가 임계치를 넘으면 테이블 크기를 늘려 재해싱(rehashing)해서 성능을 유지합니다.
트리(Tree)와 이진 탐색 트리(BST)의 차이는 무엇인가요?
  • 트리는 노드와 간선으로 이루어진 계층적 자료구조로, 자식 개수나 순서에 제약이 없습니다.
  • 이진 탐색 트리는 각 노드가 최대 2개의 자식을 가지며, 왼쪽 서브트리는 자신보다 작은 값, 오른쪽 서브트리는 자신보다 큰 값을 갖도록 정렬된 구조입니다.
  • 이 정렬 규칙 덕분에 탐색·삽입·삭제가 평균 O(log n)에 가능하지만, 데이터가 한쪽으로 치우쳐 삽입되면(예: 정렬된 데이터를 순서대로 삽입) 트리가 편향되어 최악의 경우 O(n)까지 저하됩니다.
균형 이진 탐색 트리(AVL, Red-Black Tree)는 왜 필요한가요?
  • 일반 BST는 삽입 순서에 따라 한쪽으로 치우쳐(편향) 최악의 경우 연결 리스트처럼 O(n) 성능이 될 수 있습니다.
  • AVL 트리, Red-Black 트리 같은 균형 트리는 삽입·삭제 시 회전(rotation) 연산으로 트리 높이를 O(log n)으로 유지해, 탐색·삽입·삭제 성능을 항상 O(log n)으로 보장합니다.
  • 대표적으로 Java의 TreeMap, C++의 map이 Red-Black Tree로 구현되어 있습니다.
힙(Heap)이란 무엇이며, 우선순위 큐와의 관계는 무엇인가요?
  • 힙은 부모-자식 간 대소 관계가 항상 일정하게 유지되는 완전 이진 트리입니다. 최대 힙은 부모가 자식보다 항상 크고, 최소 힙은 부모가 자식보다 항상 작습니다.
  • 루트에 항상 최댓값(또는 최솟값)이 위치하므로, 우선순위 큐를 배열 기반 힙으로 구현하면 삽입·삭제(최댓값/최솟값 꺼내기)를 O(log n)에 처리할 수 있습니다.
  • 정렬된 상태를 유지하는 BST와 달리 형제 노드 간 순서는 보장하지 않아, 전체 정렬보다 최댓값/최솟값을 반복적으로 꺼내는 상황에 특화되어 있습니다.
그래프(Graph)를 표현하는 방법에는 어떤 것들이 있고, 각각의 장단점은 무엇인가요?
  • 인접 행렬(Adjacency Matrix): 정점 개수만큼의 2차원 배열로 간선 여부를 저장합니다. 두 정점의 연결 여부를 O(1)에 확인할 수 있지만, 정점 수가 많고 간선이 적은 희소 그래프에서는 메모리 낭비가 큽니다.
  • 인접 리스트(Adjacency List): 각 정점마다 연결된 정점들의 리스트를 저장합니다. 메모리 효율이 좋아 실무·코딩테스트에서 주로 사용하지만, 두 정점의 연결 여부 확인에는 O(V)가 걸릴 수 있습니다.
Trie(트라이)는 무엇이며 어디에 사용되나요?
  • 문자열의 각 문자를 노드로 하여 공통 접두사(prefix)를 공유하는 트리 형태의 자료구조입니다.
  • 문자열 길이를 M이라 할 때 삽입·탐색이 O(M)으로, 저장된 문자열 개수와 무관하게 빠릅니다.
  • 자동완성, 사전 검색, IP 라우팅 테이블, 문자열 접두사 검색 등에 사용됩니다.
Union-Find(서로소 집합, Disjoint Set)는 무엇이며 어떤 문제에 사용되나요?
  • 원소들을 여러 그룹(집합)으로 나누어 관리하면서, 두 원소가 같은 집합인지 빠르게 판별하는 자료구조입니다.
  • find(대표 원소 찾기)와 union(두 집합 합치기) 연산을 제공합니다.
  • 경로 압축(Path Compression)과 랭크에 의한 합치기(Union by Rank)를 함께 사용하면 연산이 사실상 O(1)에 가깝게 동작합니다.
  • 크루스칼(Kruskal) 최소 신장 트리, 네트워크 연결 여부 판별, 사이클 탐지 등에 사용됩니다.

4.2 복잡도

시간 복잡도와 공간 복잡도, Big-O 표기법이란 무엇인가요?
  • 시간 복잡도는 입력 크기 n에 따라 알고리즘의 실행 시간이 어떻게 늘어나는지를 나타내고, 공간 복잡도는 추가로 필요한 메모리 사용량이 어떻게 늘어나는지를 나타냅니다.
  • Big-O 표기법은 입력이 커질 때의 점근적(asymptotic) 상한선을 나타내는 표기법으로, 상수항이나 낮은 차수의 항은 무시하고 가장 지배적인 항으로 표현합니다. (예: O(n), O(log n), O(n log n), O(n²))
  • 실제 하드웨어 성능보다, 입력이 커질수록 알고리즘 성능이 어떻게 변하는지를 비교하기 위한 척도입니다.
최선, 평균, 최악의 경우(Best, Average, Worst Case)를 구분하는 이유는 무엇인가요?
  • 같은 알고리즘도 입력 데이터의 상태에 따라 성능이 크게 달라질 수 있기 때문입니다. 예를 들어 퀵 정렬은 평균 O(n log n)이지만, 이미 정렬된 배열에 피벗을 잘못 고르면 최악의 경우 O(n²)까지 저하됩니다.
  • 일반적으로는 최악의 경우(Worst Case)를 기준으로 알고리즘의 안정성을 평가하지만, 실무에서는 평균적인 입력에서의 성능(Average Case)도 함께 고려합니다.
상각 분석(Amortized Complexity)이란 무엇인가요?
  • 한 번의 연산이 아니라, 여러 번의 연산을 수행했을 때의 평균 비용으로 시간 복잡도를 분석하는 방법입니다.
  • 예를 들어 동적 배열(ArrayList)의 add 연산은 대부분 O(1)이지만, 용량이 꽉 차면 배열을 두 배로 늘리고 기존 원소를 복사하는 O(n) 연산이 가끔 발생합니다. 이 비용을 전체 연산 횟수에 분산시키면 평균적으로 O(1)이라고 분석할 수 있습니다.

4.3 정렬과 탐색

정렬 알고리즘들을 시간 복잡도와 특징으로 비교해 주세요.
  • 버블 정렬(Bubble Sort): 인접한 두 원소를 비교해 교환. O(n²), 구현이 단순하지만 비효율적입니다.
  • 선택 정렬(Selection Sort): 매번 최솟값을 찾아 앞으로 보냄. O(n²), 교환 횟수가 적다는 장점이 있습니다.
  • 삽입 정렬(Insertion Sort): 앞에서부터 정렬된 부분에 원소를 삽입. O(n²)이지만, 거의 정렬된 데이터에서는 O(n)에 가깝게 빠릅니다.
  • 병합 정렬(Merge Sort): 분할 정복으로 반씩 나누어 정렬 후 병합. 항상 O(n log n)을 보장하는 안정 정렬이지만, 병합 과정에서 O(n)의 추가 메모리가 필요합니다.
  • 퀵 정렬(Quick Sort): 피벗을 기준으로 분할 정복. 평균 O(n log n)으로 실무에서 가장 빠르지만, 피벗을 잘못 고르면 최악 O(n²)이 되고 불안정 정렬입니다.
  • 힙 정렬(Heap Sort): 힙 자료구조를 이용해 최댓값을 하나씩 꺼내 정렬. 항상 O(n log n)이며 추가 메모리가 거의 필요 없지만, 캐시 지역성이 나빠 실제 속도는 퀵 정렬보다 느린 경우가 많습니다.
이진 탐색(Binary Search)은 어떻게 동작하며, 어떤 전제 조건이 필요한가요?
  • 정렬된 배열에서 중간값과 찾는 값을 비교해, 찾는 값이 더 크면 오른쪽 절반을, 작으면 왼쪽 절반을 탐색 범위로 좁혀가며 찾는 방식입니다.
  • 매 단계마다 탐색 범위가 절반으로 줄어들기 때문에 시간 복잡도는 O(log n)입니다.
  • 전제 조건으로 데이터가 반드시 정렬되어 있어야 합니다.

4.4 그래프 탐색과 알고리즘

DFS와 BFS의 차이, 그리고 각각 어떤 문제에 적합한가요?
  • DFS(깊이 우선 탐색)는 스택(재귀)을 사용해 한 방향으로 끝까지 탐색한 뒤 되돌아오는 방식으로, 모든 경로를 탐색하거나 백트래킹, 사이클 탐지에 적합합니다.
  • BFS(너비 우선 탐색)는 큐를 사용해 가까운 노드부터 순서대로 탐색하는 방식으로, 최단 경로(가중치가 없는 그래프)를 구할 때 적합합니다.
  • DFS는 메모리를 적게 쓰지만 최단 경로를 보장하지 않고, BFS는 최단 경로를 보장하지만 탐색 범위가 넓으면 메모리를 많이 사용할 수 있습니다.
백트래킹(Backtracking)이란 무엇이며 어떻게 동작하나요?
  • 가능한 모든 경우를 탐색하되, 조건을 만족하지 않는 경로(가지)는 더 이상 진행하지 않고 즉시 되돌아가(pruning) 탐색 범위를 줄이는 기법입니다.
  • DFS 기반으로 구현하며, N-Queen, 순열·조합 생성, 스도쿠 풀이 등에 사용됩니다. 조건 검사를 최대한 일찍 하는 것이 성능의 핵심입니다.
그리디(Greedy) 알고리즘이란 무엇이며, 언제 최적해를 보장하나요?
  • 매 단계에서 현재 상황에서 가장 좋아 보이는 선택을 하는 알고리즘입니다.
  • 전체를 다 따져보지 않기 때문에 항상 최적해를 보장하지는 않지만, 탐욕 선택 속성(현재의 최선 선택이 전체 최적해에 포함됨)과 최적 부분 구조를 만족하는 문제에서는 최적해를 보장합니다. (예: 거스름돈 문제, 최소 신장 트리, 활동 선택 문제)
동적 계획법(Dynamic Programming)이란 무엇이며, 그리디와의 차이는 무엇인가요?
  • 큰 문제를 작은 부분 문제로 나누고, 부분 문제의 답을 저장(메모이제이션)해 재사용함으로써 중복 계산을 줄이는 기법입니다.
  • 최적 부분 구조(부분 문제의 최적해로 전체 최적해를 구성 가능)와 중복되는 부분 문제라는 두 조건을 만족할 때 사용할 수 있습니다.
  • 그리디는 한 번 선택하면 되돌아보지 않지만, DP는 모든 부분 문제의 결과를 비교해 최적해를 찾기 때문에 그리디로 풀리지 않는 문제도 정확하게 풀 수 있습니다. (예: 배낭 문제, 최장 공통 부분 수열)
  • Top-down(메모이제이션)은 재귀 + 캐싱으로 필요한 부분 문제만 계산하고, Bottom-up(타뷸레이션)은 반복문으로 작은 부분 문제부터 차례로 채워나갑니다.
다익스트라(Dijkstra) 알고리즘은 무엇이며, 어떤 한계가 있나요?
  • 하나의 시작 정점에서 다른 모든 정점까지의 최단 경로를 구하는 알고리즘으로, 아직 확정되지 않은 정점 중 거리가 가장 짧은 정점을 우선순위 큐로 뽑아가며 거리를 갱신(완화, relaxation)합니다.
  • 우선순위 큐(힙)를 사용하면 O((V + E) log V)에 동작합니다.
  • 간선의 가중치가 음수이면 정확한 결과를 보장하지 못한다는 한계가 있습니다. 음수 가중치가 있는 경우에는 벨만-포드(Bellman-Ford) 알고리즘을 사용해야 합니다.
플로이드-워셜(Floyd–Warshall) 알고리즘은 다익스트라와 무엇이 다른가요?
  • 다익스트라가 한 정점에서 다른 모든 정점까지의 최단 거리(단일 출발점)를 구하는 반면, 플로이드-워셜은 모든 정점 쌍 간의 최단 거리를 한 번에 구합니다.
  • 각 정점을 경유지로 하나씩 추가해가며 더 짧은 경로가 있는지 갱신하는 방식(DP)으로 동작하며, 시간 복잡도는 O(V³)입니다.
  • 정점 수가 많지 않을 때, 모든 쌍의 최단 거리가 필요한 경우에 적합하며 음수 가중치도 처리할 수 있습니다(음수 사이클은 불가).
최소 신장 트리(Minimum Spanning Tree)란 무엇이며, 어떻게 구하나요?
  • 그래프의 모든 정점을 사이클 없이 최소 비용의 간선들로 연결한 부분 그래프입니다.
  • 크루스칼(Kruskal): 간선을 가중치 오름차순으로 정렬한 뒤, Union-Find로 사이클이 생기지 않는 간선만 순서대로 선택합니다.
  • 프림(Prim): 임의의 정점에서 시작해, 현재 트리에 연결된 간선 중 가장 가중치가 작은 간선을 우선순위 큐로 하나씩 추가해 나갑니다.
  • 간선이 적은 희소 그래프에는 크루스칼이, 간선이 많은 밀집 그래프에는 프림이 유리한 편입니다.
위상 정렬(Topological Sort)이란 무엇이며 언제 사용하나요?
  • 방향 그래프에서 간선의 방향을 거스르지 않도록 정점들을 일렬로 나열하는 정렬 방식입니다.
  • 사이클이 없는 방향 그래프(DAG, Directed Acyclic Graph)에서만 가능합니다.
  • 진입 차수(in-degree)가 0인 정점부터 큐에 넣고 하나씩 제거하며 연결된 정점의 진입 차수를 줄여나가는 칸(Kahn) 알고리즘, 또는 DFS 후 완료 순서의 역순으로 구할 수 있습니다.
  • 작업 스케줄링, 빌드 의존성 해결, 강의 선수 과목 순서 결정 등에 사용됩니다.

5. 웹

5.1 웹 구조

Client와 Server 구조란 무엇인가요?
  • 서비스를 요청하는 쪽(Client)과 요청을 받아 처리 후 응답하는 쪽(Server)으로 역할을 나눈 구조입니다.
  • 클라이언트는 UI·사용자 입력을 담당하고, 서버는 데이터·비즈니스 로직을 담당해 역할을 분리함으로써 유지보수성과 확장성을 높입니다.
정적 웹과 동적 웹의 차이는 무엇인가요?
  • 정적 웹: 미리 만들어진 HTML 파일을 그대로 응답합니다. 모든 사용자에게 같은 내용이 보이고, 서버 부담이 적습니다.
  • 동적 웹: 요청 시점에 서버(또는 클라이언트)에서 데이터를 조합해 페이지를 생성합니다. 사용자·상황에 따라 다른 내용을 보여줄 수 있습니다.
CSR과 SSR의 차이는 무엇인가요?
  • CSR(Client-Side Rendering): 서버는 최소한의 HTML과 JS를 내려주고, 브라우저가 JS를 실행해 화면을 그립니다. 초기 로딩은 느리지만 이후 페이지 전환이 빠르고 서버 부담이 적습니다.
  • SSR(Server-Side Rendering): 서버가 완성된 HTML을 만들어 내려줍니다. 초기 로딩과 SEO에 유리하지만 매 요청마다 서버 렌더링 비용이 발생합니다.
SSG는 무엇이며 CSR·SSR과 어떻게 다른가요?
  • SSG(Static Site Generation): 빌드 시점에 미리 HTML을 생성해 두고, 요청 시에는 이미 만들어진 정적 파일을 그대로 응답합니다.
  • 요청마다 렌더링하는 SSR보다 훨씬 빠르고 서버 부담이 없지만, 콘텐츠가 자주 바뀌면 매번 다시 빌드해야 한다는 한계가 있습니다. (블로그, 문서 사이트 등에 적합)
SPA란 무엇이며 장단점은 무엇인가요?
  • SPA(Single Page Application): 하나의 HTML 페이지를 로드한 뒤, 이후 화면 전환은 서버로부터 전체 페이지를 다시 받지 않고 JS로 필요한 부분만 갱신하는 방식입니다.
  • 장점: 페이지 전환이 빠르고 앱과 유사한 UX를 제공합니다.
  • 단점: 초기 로딩(번들 크기)이 크고, 별도 처리를 하지 않으면 SEO에 불리합니다.
서버 컴포넌트(Server Component)란 무엇인가요?
  • 서버에서만 실행되고 렌더링 결과만 클라이언트로 전달되는 컴포넌트입니다. React Server Components가 대표적이며 Next.js App Router에서 기본값으로 사용됩니다.
  • 컴포넌트 코드 자체가 클라이언트 번들에 포함되지 않으므로, 무거운 라이브러리를 써도 사용자가 내려받는 JS 양이 늘어나지 않습니다.
  • 서버에서 실행되기 때문에 DB나 파일 시스템에 직접 접근할 수 있어, 단순 조회를 위해 별도 API를 만들지 않아도 됩니다.
  • 대신 상태나 이벤트 핸들러, 브라우저 API는 사용할 수 없습니다. 상호작용이 필요한 부분만 클라이언트 컴포넌트로 분리해 조합하는 방식으로 설계합니다.
서버 컴포넌트와 SSR은 어떻게 다른가요?
  • SSR: 서버에서 HTML을 만들어 내려주지만, 상호작용을 위해 해당 컴포넌트의 JS도 함께 전달되어 브라우저에서 다시 실행(하이드레이션)됩니다. 즉 서버와 클라이언트에서 두 번 실행됩니다.
  • 서버 컴포넌트: 서버에서만 실행되고 그 JS는 아예 클라이언트로 전달되지 않으므로 하이드레이션 대상이 아닙니다.
  • 그래서 SSR은 초기 렌더링 속도를 개선하는 기법이고, 서버 컴포넌트는 클라이언트로 보내는 코드의 양 자체를 줄이는 접근이라고 구분할 수 있습니다.
REST API, RPC, GraphQL의 차이는 무엇인가요?
  • REST API: 자원(Resource)을 URI로 표현하고 HTTP Method(GET/POST/PUT/DELETE)로 행위를 나타내는 방식입니다.
  • RPC(Remote Procedure Call): 원격 함수를 호출하는 것처럼 동작을 URI/메서드명으로 직접 표현합니다. (예: /getUser) 자원보다 행위 중심입니다.
  • GraphQL: 클라이언트가 필요한 데이터의 형태를 쿼리로 직접 명시해 요청하는 방식으로, 하나의 엔드포인트에서 필요한 데이터만 정확히 받아 Over-fetching/Under-fetching 문제를 줄일 수 있습니다.
CORS는 왜 발생하나요?
  • 브라우저는 보안을 위해 기본적으로 다른 출처(Origin: 프로토콜+도메인+포트)로의 요청을 제한하는 동일 출처 정책(Same-Origin Policy)을 따릅니다.
  • 프론트엔드와 백엔드의 출처가 다른 경우(예: localhost:3000에서 api.example.com 호출) 브라우저가 이 정책 때문에 응답을 차단하면서 CORS 에러가 발생합니다.
  • 서버가 응답 헤더에 Access-Control-Allow-Origin 등을 명시해 허용한 출처임을 알려주면 브라우저가 응답을 정상적으로 전달합니다.
비동기 통신이란 무엇이며 어떤 방식들이 있나요?
  • 요청을 보낸 뒤 응답을 기다리는 동안 화면이 멈추지 않고, 응답이 도착하면 필요한 부분만 갱신하는 방식입니다. 페이지 전체를 다시 불러오지 않아도 됩니다.
  • AJAX / fetch: 필요한 시점에 요청을 보내고 응답을 받는 가장 기본적인 방식입니다.
  • Polling: 클라이언트가 주기적으로 서버에 새 데이터가 있는지 물어봅니다. 구현이 단순하지만 변경이 없어도 요청이 계속 발생해 낭비가 큽니다.
  • Long Polling: 요청을 받은 서버가 새 데이터가 생길 때까지 응답을 미룹니다. 불필요한 요청은 줄지만 연결을 오래 붙잡습니다.
  • SSE: 서버가 클라이언트로 데이터를 계속 밀어주는 단방향 스트림입니다.
  • WebSocket: 하나의 연결에서 양방향으로 주고받습니다.
  • 선택 기준은 실시간성이 얼마나 필요한지와 통신 방향입니다. 서버에서 내려주기만 하면 되면 SSE, 양방향이 필요하면 WebSocket이 적합합니다.
SSE란 무엇인가요?
  • 서버가 클라이언트로 실시간 이벤트를 지속 전송하는 단방향 통신입니다.
  • HTTP 연결을 유지하며 text/event-stream을 사용합니다.
  • 알림, 실시간 로그, 주식 시세 등에 적합합니다.
  • 클라이언트 → 서버 메시지는 일반 HTTP 요청으로 별도 전송합니다.
WebSocket이란 무엇인가요?
  • 클라이언트와 서버가 하나의 연결에서 양방향으로 실시간 통신하는 프로토콜입니다.
  • 최초에는 HTTP Handshake 후 WebSocket 연결로 전환됩니다.
  • 채팅, 게임, 협업 편집처럼 양방향·저지연 통신에 적합합니다.
  • 연결 상태 관리와 확장성 설계가 필요합니다.

5.2 REST

REST란 무엇이며 왜 Resource 중심으로 설계하나요?
  • REST(Representational State Transfer)는 자원을 URI로 표현하고, 자원에 대한 행위를 HTTP Method로 표현하는 API 설계 스타일입니다.
  • 행위(동사) 대신 자원(명사) 중심으로 URI를 설계하면, 같은 자원에 대해 조회·생성·수정·삭제를 일관된 규칙(Method)으로 다룰 수 있어 API가 예측 가능하고 일관성 있어집니다.
RESTful한 URI 설계 원칙은 무엇인가요?
  • URI에는 행위(동사)가 아닌 자원(명사)을 사용합니다. (예: /users가 아니라 /getUsers 사용 X)
  • 자원의 계층 관계는 경로로 표현합니다. (예: /users/1/orders)
  • 소문자와 하이픈을 사용하고, 일관된 규칙(단수/복수 등)을 유지합니다.
REST API에서 HTTP Method는 어떻게 활용하나요?
  • GET: 자원 조회, POST: 자원 생성, PUT: 자원 전체 수정(또는 생성), PATCH: 자원 일부 수정, DELETE: 자원 삭제로 역할을 나눠 사용합니다.
  • 같은 URI라도 Method에 따라 의도가 명확해져, 별도 설명 없이도 API의 동작을 예측할 수 있습니다.
REST API가 Stateless해야 하는 이유는 무엇인가요?
  • 서버가 클라이언트의 이전 요청 상태를 기억하지 않고, 매 요청에 필요한 정보(인증 토큰 등)를 요청 자체에 포함시키는 설계 원칙입니다.
  • 서버가 상태를 갖지 않으므로 서버를 여러 대로 자유롭게 확장(Scale-out)할 수 있고, 특정 요청이 특정 서버로만 가야 하는 제약이 없어집니다.
REST API에서 Idempotency(멱등성)는 왜 중요한가요?
  • 같은 요청을 여러 번 보내도 서버의 최종 상태가 동일하게 유지되는 성질입니다. (GET, PUT, DELETE는 멱등, POST는 비멱등)
  • 네트워크 오류로 클라이언트가 응답을 못 받고 요청을 재시도할 때, 멱등한 API는 중복 실행되어도 안전하기 때문에 재시도 로직을 안심하고 둘 수 있습니다.
API Pagination은 어떻게 구현하나요?
  • Offset 기반: page, size(또는 limit, offset) 파라미터로 원하는 구간을 조회합니다. 구현이 간단하지만 뒤 페이지일수록 성능이 떨어집니다.
  • Cursor(Keyset) 기반: 마지막으로 받은 항목의 식별자를 기준으로 다음 페이지를 요청합니다. 데이터가 많아도 성능이 일정하게 유지되어 무한 스크롤 등에 적합합니다.
API Versioning은 왜, 어떻게 하나요?
  • API 스펙이 바뀌어도 기존 클라이언트가 깨지지 않도록 하기 위해 버전을 관리합니다.
  • URI에 버전을 명시(/v1/users)하거나, 요청 헤더에 버전 정보를 담는 방식을 주로 사용합니다.
REST API의 Error Response는 어떻게 설계해야 하나요?
  • HTTP Status Code로 에러의 큰 분류(4xx: 클라이언트 오류, 5xx: 서버 오류)를 먼저 나타냅니다.
  • 응답 본문에는 에러 코드, 사람이 읽을 수 있는 메시지, 필요하면 상세 필드 정보를 포함해 클라이언트가 원인을 구분하고 처리할 수 있게 일관된 형식으로 설계합니다.

5.3 인증과 인가

Authentication과 Authorization의 차이는 무엇인가요?
  • Authentication(인증): 사용자가 누구인지 확인하는 과정입니다. (로그인)
  • Authorization(인가): 인증된 사용자가 특정 자원·기능에 접근할 권한이 있는지 확인하는 과정입니다.
  • 인증이 선행되어야 인가를 판단할 수 있는 관계입니다.
쿠키와 세션의 차이는 무엇인가요?
  • 쿠키: 브라우저에 저장되는 작은 데이터이며, 요청 시 서버로 전송됩니다.
  • 세션: 사용자 상태를 서버에 저장하고, 클라이언트는 세션 ID만 보관합니다.
  • 쿠키는 클라이언트 저장, 세션은 서버 저장이 핵심 차이입니다.
  • 세션 ID는 주로 쿠키에 담아 전달합니다.
세션 기반 인증과 토큰 기반 인증은 어떻게 다른가요?
  • 세션 기반 인증: 로그인 성공 시 서버가 세션을 생성해 저장하고, 클라이언트에는 세션 ID만 내려줍니다. 서버가 상태를 들고 있어야 하므로(Stateful) 서버 확장 시 세션 공유(스티키 세션, 세션 저장소 등)가 필요합니다.
  • 토큰 기반 인증: 로그인 성공 시 서버가 토큰(JWT 등)을 발급하고, 서버는 상태를 저장하지 않습니다(Stateless). 클라이언트가 매 요청마다 토큰을 함께 보내며, 서버는 토큰 자체의 서명만 검증하면 되어 수평 확장에 유리합니다.
JWT란 무엇인가요?
  • JSON 기반의 토큰 형식으로, 사용자 인증·인가 정보에 사용됩니다.
  • Header.Payload.Signature 구조이며 서버의 서명으로 위변조를 검증합니다.
  • 서버가 세션 상태를 저장하지 않는 Stateless 인증에 적합합니다.
  • Payload는 암호화가 아니므로 민감 정보를 넣으면 안 됩니다.
세션과 JWT 중 무엇을 선택해야 하나요?
  • 세션: 인증 상태를 서버가 들고 있으므로 강제 로그아웃이나 권한 회수를 즉시 반영할 수 있습니다. 대신 서버를 늘리면 세션을 공유할 저장소(Redis 등)가 필요하고, 매 요청마다 저장소를 조회하는 비용이 듭니다.
  • JWT: 서버가 상태를 저장하지 않고 서명만 검증하면 되므로 확장이 쉽고, 여러 서비스가 같은 토큰을 검증할 수 있습니다. 대신 한 번 발급하면 만료 전까지 무효화하기 어렵습니다.
  • JWT의 이 무효화 문제 때문에 Access Token의 수명을 짧게 두고 Refresh Token으로 갱신하는 구조를 사용합니다. 즉시 차단이 꼭 필요하면 블랙리스트를 두는데, 그러면 다시 서버가 상태를 갖게 되어 Stateless라는 장점이 옅어집니다.
  • 그 밖에 JWT는 매 요청 헤더에 실려 세션 ID보다 크기가 크고, 페이로드는 서명만 되어 있을 뿐 암호화가 아니라는 점도 고려해야 합니다.
  • 정리하면 단일 서비스이면서 즉각적인 세션 통제가 중요하면 세션이, 서버 확장이나 여러 서비스·모바일 클라이언트를 함께 지원해야 하면 JWT가 유리합니다.
Access Token과 Refresh Token은 왜 분리하나요?
  • Access Token: 실제 API 요청 시 사용하는 토큰으로, 탈취 피해를 줄이기 위해 만료 시간을 짧게 둡니다.
  • Refresh Token: Access Token이 만료되었을 때 재발급받기 위한 토큰으로, 만료 시간이 길지만 안전한 저장소(예: HttpOnly 쿠키)에 보관하고 노출 범위를 최소화합니다.
  • 두 토큰을 분리하면 짧은 Access Token으로 보안을 강화하면서도, 사용자가 매번 재로그인하지 않는 편의성을 함께 얻을 수 있습니다.
OAuth 2.0이란 무엇인가요?
  • 사용자가 자신의 비밀번호를 제3자 서비스에 직접 노출하지 않고, 특정 권한(Scope)만 위임해 다른 서비스가 자신의 자원에 접근하도록 허용하는 인가 프로토콜입니다.
  • Google/GitHub 소셜 로그인이 대표적인 활용 예이며, 인증 서버가 발급한 Access Token으로 리소스 서버에 접근합니다.
RBAC란 무엇인가요?
  • Role-Based Access Control, 역할 기반 접근 제어입니다.
  • 사용자에게 직접 권한을 부여하지 않고 역할(Role)을 부여한 뒤, 역할에 권한을 매핑해서 관리합니다.
  • 사용자 수가 많아져도 역할 단위로 권한을 관리할 수 있어 유지보수가 쉬워집니다.

5.4 서버 구조

Web Server와 WAS(Application Server)의 차이는 무엇인가요?
  • Web Server: 정적 자원(HTML, CSS, 이미지)을 처리하고 응답하는 서버입니다. (예: Nginx, Apache)
  • WAS(Web Application Server): 비즈니스 로직을 실행해 동적인 콘텐츠를 생성하는 서버입니다. (예: Tomcat)
  • 실무에서는 Web Server가 정적 자원과 리버스 프록시 역할을 맡고, 동적 요청만 WAS로 전달하는 구조를 많이 사용합니다.
Connection Pool은 왜 필요한가요?
  • DB나 외부 서비스에 매번 새로 연결을 맺으면 연결 수립(TCP, 인증 등) 비용이 크기 때문에, 미리 연결을 여러 개 만들어 두고 재사용하기 위해 사용합니다.
  • 연결 생성 비용을 줄이고, 동시 연결 수를 제한해 자원(DB 등)을 보호하는 효과도 있습니다.
Thread Pool은 무엇이며 왜 사용하나요?
  • 요청마다 스레드를 새로 생성·소멸시키는 대신, 미리 만들어 둔 스레드를 재사용하는 방식입니다.
  • 스레드 생성·소멸 비용을 줄이고, 동시에 실행되는 스레드 수를 제한해 서버 자원이 과도하게 소모되는 것을 방지합니다.
Scale-up과 Scale-out의 차이는 무엇인가요?
  • Scale-up(수직 확장): 서버 한 대의 CPU·메모리 등 성능을 높이는 방식입니다. 구성이 단순하지만 한계가 있고 단일 장애점이 됩니다.
  • Scale-out(수평 확장): 서버 대수를 늘려 트래픽을 분산하는 방식입니다. 이론상 확장에 한계가 적지만, 로드 밸런서와 상태 공유(Stateless 설계) 같은 추가 구성이 필요합니다.
서버를 Stateless하게 설계해야 하는 이유는 무엇인가요?
  • 서버가 클라이언트별 상태(세션 등)를 갖고 있지 않으면, 어떤 요청이든 어떤 서버 인스턴스로 가도 동일하게 처리할 수 있습니다.
  • 이 덕분에 로드 밸런서 뒤에서 서버를 자유롭게 추가·제거하는 Scale-out이 쉬워지고, 특정 서버 장애 시에도 다른 서버가 그대로 대체할 수 있습니다.
Cache는 어떤 계층에서 사용할 수 있나요?
  • 브라우저 캐시(정적 자원), CDN 캐시(엣지 서버), 애플리케이션 캐시(Redis 등), DB 쿼리 캐시까지 여러 계층에서 사용할 수 있습니다.
  • 자주 조회되지만 자주 바뀌지 않는 데이터를 캐싱해 응답 속도를 높이고 원본 서버·DB의 부하를 줄이는 것이 핵심 목적이며, 캐시 최신성(무효화 전략)을 함께 고려해야 합니다.
CDN이란 무엇인가요?
  • Content Delivery Network, 전 세계 여러 위치에 분산된 서버에 콘텐츠(정적 자원 등)를 캐싱해 두고, 사용자와 지리적으로 가까운 서버에서 응답하도록 하는 시스템입니다.
  • 지연 시간을 줄이고, 원본 서버로 가는 트래픽을 줄여 부하를 분산하는 효과가 있습니다.
프록시와 리버스 프록시의 차이는 무엇인가요?
  • 프록시(Forward Proxy): 클라이언트 앞에 위치해 클라이언트를 대신하여 서버에 요청합니다.
  • 클라이언트 IP 은닉, 접근 제어, 캐싱 등에 사용합니다.
  • 리버스 프록시: 서버 앞에 위치해 서버를 대신하여 클라이언트 요청을 받습니다.
  • 로드 밸런싱, SSL 종료, 캐싱, 서버 IP 은닉 등에 사용합니다.
Load Balancer란 무엇인가요?
  • 여러 서버에 요청을 분산해 트래픽을 처리하는 장비 또는 소프트웨어입니다.
  • 서버 장애 시 정상 서버로만 보내는 헬스 체크를 수행합니다.
  • 성능, 가용성, 확장성을 높입니다.
  • 대표 방식: Round Robin, Least Connections, IP Hash.
백그라운드 작업 큐(Task Queue)는 왜 사용하나요?
  • 이미지 변환이나 메일 발송처럼 오래 걸리는 작업을 요청-응답 흐름에서 분리하기 위해 사용합니다. 서버는 작업을 큐에 넣고 즉시 응답하며, 실제 처리는 워커가 나중에 수행합니다.
  • 구조는 작업을 넣는 애플리케이션(Producer), 작업을 보관하는 브로커(Broker), 꺼내서 실행하는 워커(Worker)로 나뉩니다.
  • 트래픽이 몰려도 큐가 완충 역할을 하고, 실패한 작업을 재시도하거나 워커 수를 늘려 처리량을 조절할 수 있습니다.
  • 워커가 같은 작업을 재시도할 수 있으므로 작업은 멱등하게 설계하는 것이 안전합니다.
작업 큐의 브로커로 쓰이는 대표 기술 두 가지를 비교해 주세요.
  • RabbitMQ: 메시지 브로커 전용 솔루션입니다. 처리 완료 응답(ACK)을 받아야 메시지를 제거하므로 유실 없이 전달하는 데 강하고, 조건에 따라 큐를 나누는 라우팅 기능이 유연합니다. 대신 운영해야 할 구성 요소가 늘어납니다.
  • Redis: 인메모리 저장소를 큐로 활용하는 방식입니다. 매우 빠르고 이미 캐시로 쓰고 있다면 추가 도입 부담이 적습니다. 대신 메모리 기반이라 장애 시 유실 가능성이 있고 전달 보장 기능이 상대적으로 약합니다.
  • 유실되면 안 되는 결제·정산 작업이라면 RabbitMQ가, 손실을 감수할 수 있는 알림이나 캐시 갱신이라면 Redis가 적합합니다.
  • 참고로 Kafka도 자주 언급되는데, 소비한 메시지를 지우지 않고 로그로 보존해 여러 소비자가 각자 읽고 과거 데이터를 재처리할 수 있다는 점에서 작업 큐보다는 이벤트 스트리밍에 가깝습니다.

5.5 자바스크립트

자바스크립트의 비동기 동작 원리를 설명해 주세요.
  • 자바스크립트 엔진은 싱글 스레드로, 호출 스택(Call Stack)이 하나뿐이라 한 번에 하나의 작업만 실행합니다.
  • setTimeout이나 네트워크 요청 같은 비동기 작업은 엔진이 직접 처리하지 않고 런타임(브라우저의 Web API, Node.js의 libuv)에 맡깁니다. 그래서 엔진은 기다리지 않고 다음 코드를 계속 실행합니다.
  • 런타임에서 작업이 끝나면 등록해 둔 콜백이 큐에 들어갑니다.
  • 이벤트 루프는 호출 스택이 비어 있는지 확인하다가, 비면 큐에서 콜백을 꺼내 스택에 올립니다.
  • 이 구조 덕분에 싱글 스레드지만 대기 작업에 막히지 않습니다. 다만 오래 걸리는 동기 연산은 스택을 계속 점유하므로 그동안 모든 처리가 멈춥니다.
매크로태스크 큐와 마이크로태스크 큐는 어떻게 다른가요?
  • 매크로태스크 큐(태스크 큐): setTimeout, setInterval, I/O 완료 콜백 등이 들어갑니다.
  • 마이크로태스크 큐: Promise의 then·catch·finally, queueMicrotask 등이 들어갑니다. async/await에서 await 이후 이어지는 코드도 여기에 해당합니다.
  • 우선순위가 다릅니다. 이벤트 루프는 매크로태스크를 하나 실행한 뒤 마이크로태스크 큐를 전부 비우고 나서야 다음 매크로태스크로 넘어갑니다.
  • 그래서 setTimeout(fn, 0)보다 Promise.resolve().then(fn)이 항상 먼저 실행됩니다.
  • 주의할 점은 마이크로태스크가 계속 새로운 마이크로태스크를 등록하면 큐가 비지 않아, 다음 매크로태스크와 화면 렌더링이 계속 밀린다는 것입니다.

5.6 웹 보안

XSS는 무엇이며 어떻게 방어하나요?
  • 공격자가 삽입한 스크립트가 다른 사용자의 브라우저에서 실행되는 취약점입니다. 이를 통해 쿠키나 토큰을 탈취하거나 사용자 몰래 요청을 보낼 수 있습니다.
  • 게시글처럼 저장된 값이 다른 사용자에게 노출되는 형태, URL 파라미터가 그대로 화면에 출력되는 형태 등이 있습니다.
  • 방어의 핵심은 출력할 때 이스케이프하는 것입니다. 입력을 막는 것보다 화면에 그릴 때 태그로 해석되지 않게 처리하는 것이 확실합니다.
  • 추가로 CSP 헤더로 실행 가능한 스크립트 출처를 제한하고, 쿠키에 HttpOnly를 설정해 탈취 피해를 줄입니다.
SQL Injection은 무엇이며 어떻게 방어하나요?
  • 사용자 입력이 SQL 문자열에 그대로 붙어, 쿼리의 구조 자체가 공격자가 의도한 대로 바뀌는 취약점입니다.
  • 근본적인 방어는 PreparedStatement처럼 값을 파라미터로 바인딩하는 것입니다. 이 경우 입력은 항상 데이터로만 취급되어 쿼리 구조를 바꿀 수 없습니다.
  • ORM을 쓰더라도 문자열을 직접 조합해 네이티브 쿼리를 만들면 똑같이 취약합니다.
  • 부가적으로 애플리케이션 DB 계정에 최소 권한만 부여하고, 에러 메시지에 쿼리나 스키마 정보가 노출되지 않도록 합니다.
비밀번호는 어떻게 저장해야 하나요?
  • 평문 저장은 물론이고 복호화 가능한 암호화도 적절하지 않습니다. 되돌릴 수 없는 단방향 해시를 사용해야 합니다.
  • MD5나 SHA 같은 범용 해시는 연산이 너무 빨라 무차별 대입에 취약하므로 부적절합니다.
  • 사용자마다 다른 Salt를 붙여, 같은 비밀번호라도 다른 해시가 나오게 하고 미리 계산된 표를 무력화합니다.
  • BCrypt나 Argon2처럼 의도적으로 느리고 연산 강도를 조절할 수 있는 알고리즘을 사용하는 것이 표준입니다.
XSS와 CSRF는 어떻게 다른가요?
  • XSS: 사용자가 그 사이트를 신뢰한다는 점을 악용해, 사이트를 통해 악성 스크립트를 실행시킵니다.
  • CSRF: 사이트가 그 사용자를 신뢰한다는 점을 악용해, 로그인된 사용자가 의도하지 않은 요청을 보내게 만듭니다.
  • 둘의 관계도 중요합니다. XSS가 뚫리면 공격자가 페이지 안에서 CSRF 토큰을 읽어낼 수 있어 CSRF 방어까지 무력화됩니다. 그래서 XSS 방어가 더 근본적입니다.
인증 토큰은 어디에 저장해야 하나요?
  • localStorage: 자바스크립트로 접근할 수 있어 XSS가 발생하면 그대로 탈취됩니다.
  • HttpOnly 쿠키: 자바스크립트가 접근할 수 없어 XSS에 안전하지만, 브라우저가 자동 전송하므로 CSRF를 고려해야 합니다. SameSite 속성으로 상당 부분 완화할 수 있습니다.
  • 실무에서는 수명이 긴 Refresh Token은 HttpOnly·Secure·SameSite 쿠키에 두고, 수명이 짧은 Access Token은 메모리에 유지하는 조합을 많이 사용합니다.
Rate Limiting은 왜 필요한가요?
  • 로그인 무차별 대입, 과도한 크롤링, 특정 사용자의 자원 독점을 막아 서비스를 보호합니다.
  • 고정 시간 단위로 세는 방식은 구현이 쉽지만 경계 시점에 순간적으로 두 배의 요청이 통과할 수 있습니다. 슬라이딩 윈도우나 토큰 버킷 방식이 더 정확합니다.
  • 서버가 여러 대라면 각 서버가 따로 세면 의미가 없으므로, Redis 같은 공유 저장소에 카운트를 두어야 합니다.

6. AI

6.1 머신러닝 기초

지도학습, 비지도학습, 강화학습의 차이는 무엇인가요?
  • 지도학습(Supervised Learning): 입력과 정답(label)이 함께 주어진 데이터로 학습합니다. (분류, 회귀)
  • 비지도학습(Unsupervised Learning): 정답 없이 데이터 자체의 패턴이나 구조를 찾습니다. (군집화, 차원 축소)
  • 강화학습(Reinforcement Learning): 에이전트가 환경과 상호작용하며 보상(Reward)을 최대화하는 행동을 학습합니다.
분류(Classification)와 회귀(Regression)의 차이는 무엇인가요?
  • 분류: 출력이 정해진 범주(카테고리) 중 하나인 문제입니다. (예: 스팸/정상 메일 판별)
  • 회귀: 출력이 연속적인 값인 문제입니다. (예: 집값 예측)
학습 데이터, 검증 데이터, 테스트 데이터는 왜 나누나요?
  • 학습(Train) 데이터: 모델 파라미터를 직접 학습시키는 데 사용합니다.
  • 검증(Validation) 데이터: 학습 중 하이퍼파라미터 튜닝과 모델 선택 기준으로 사용합니다.
  • 테스트(Test) 데이터: 학습에 전혀 사용하지 않은 데이터로, 최종 모델의 일반화 성능을 평가합니다.
  • 같은 데이터로 학습과 평가를 모두 하면 성능을 과대평가하게 되므로 반드시 분리해야 합니다.
Overfitting과 Underfitting은 무엇이며 어떻게 완화하나요?
  • Overfitting(과적합): 모델이 학습 데이터의 노이즈까지 지나치게 학습해, 학습 데이터에는 잘 맞지만 새로운 데이터에는 성능이 떨어지는 상태입니다. 완화: 데이터 추가, 정규화(L1/L2), Dropout, 모델 복잡도 축소, Early Stopping 등을 사용합니다.
  • Underfitting(과소적합): 모델이 너무 단순해 학습 데이터의 패턴조차 제대로 못 학습한 상태입니다. 완화: 모델 복잡도(레이어·파라미터) 증가, 학습을 더 오래 진행, 피처 추가 등을 사용합니다.
Bias와 Variance의 관계(Trade-off)는 무엇인가요?
  • Bias(편향): 모델이 실제 정답과 얼마나 다르게 예측하는지를 나타내며, 편향이 크면 Underfitting으로 이어집니다.
  • Variance(분산): 학습 데이터가 바뀔 때 모델 예측이 얼마나 크게 흔들리는지를 나타내며, 분산이 크면 Overfitting으로 이어집니다.
  • 모델 복잡도를 높이면 Bias는 줄지만 Variance는 커지는 트레이드오프가 있어, 둘의 균형을 맞추는 것이 목표입니다.
Precision, Recall, F1-score는 무엇인가요?
  • Precision(정밀도): 모델이 Positive로 예측한 것 중 실제로 Positive인 비율입니다. (TP / (TP+FP))
  • Recall(재현율): 실제 Positive 중 모델이 Positive로 맞춘 비율입니다. (TP / (TP+FN))
  • F1-score: Precision과 Recall의 조화평균으로, 둘 사이의 트레이드오프를 하나의 지표로 종합합니다.
  • 데이터가 불균형할 때(예: 이상 탐지) Accuracy보다 이 지표들이 더 의미 있는 평가 기준이 됩니다.
ROC-AUC란 무엇인가요?
  • ROC 커브는 분류 임계값(threshold)을 바꿔가며 True Positive Rate(재현율)와 False Positive Rate의 변화를 그린 곡선입니다.
  • AUC(Area Under Curve)는 이 곡선 아래 면적으로, 1에 가까울수록 모델이 Positive/Negative를 잘 구분한다는 의미입니다. 임계값에 의존하지 않고 모델 자체의 분류 성능을 비교할 수 있습니다.

6.2 딥러닝 기초

신경망(Neural Network)의 기본 구조는 무엇인가요?
  • 입력층, 하나 이상의 은닉층, 출력층으로 구성되며, 각 층은 여러 노드(뉴런)로 이루어집니다.
  • 각 노드는 이전 층의 출력에 가중치를 곱하고 편향을 더한 뒤, 활성화 함수를 통과시켜 다음 층으로 전달합니다.
활성화 함수(Activation Function)는 왜 필요한가요?
  • 입력에 비선형성을 부여해, 신경망이 단순한 선형 결합만으로는 표현할 수 없는 복잡한 패턴을 학습할 수 있게 합니다.
  • 활성화 함수가 없다면 층을 아무리 쌓아도 결국 하나의 선형 함수와 동일해집니다. (대표: ReLU, Sigmoid, Softmax)
Loss Function이란 무엇인가요?
  • 모델의 예측값과 실제 정답이 얼마나 차이 나는지를 하나의 수치로 나타내는 함수입니다.
  • 학습은 이 손실 값을 최소화하는 방향으로 파라미터를 갱신하는 과정입니다. (예: 회귀는 MSE, 분류는 Cross Entropy)
Gradient Descent는 어떻게 동작하나요?
  • 손실 함수를 파라미터에 대해 미분(기울기)한 뒤, 손실이 줄어드는 방향(기울기의 반대 방향)으로 파라미터를 조금씩 이동시키는 최적화 방법입니다.
  • 이동하는 정도는 Learning Rate로 조절합니다.
Backpropagation이란 무엇인가요?
  • 신경망의 출력에서 발생한 손실을 체인 룰(연쇄 법칙)을 이용해 출력층부터 입력층 방향으로 거슬러 전파하며, 각 파라미터에 대한 기울기를 효율적으로 계산하는 알고리즘입니다.
  • 이렇게 구한 기울기를 Gradient Descent에 사용해 파라미터를 갱신합니다.
Batch, Epoch, Learning Rate는 각각 무엇인가요?
  • Batch: 한 번의 파라미터 갱신에 사용하는 데이터 묶음의 크기입니다.
  • Epoch: 전체 학습 데이터를 한 번 모두 학습에 사용한 횟수입니다.
  • Learning Rate: 파라미터를 한 번에 얼마나 크게 갱신할지 정하는 값으로, 너무 크면 학습이 불안정해지고 너무 작으면 학습이 느려집니다.
CNN, RNN, Transformer는 각각 어떤 문제에 적합하며 왜 등장했나요?
  • CNN: 필터(커널)로 지역적인 특징(공간적 패턴)을 추출해, 이미지처럼 위치 정보가 중요한 데이터에 적합합니다.
  • RNN: 이전 시점의 은닉 상태를 다음 시점으로 전달해 순차 데이터(텍스트, 시계열)를 처리하지만, 시퀀스가 길어지면 초반 정보가 희석되는 장기 의존성 문제가 있습니다.
  • Transformer: RNN의 순차 처리 한계를 극복하기 위해, Self-Attention으로 시퀀스의 모든 위치를 한 번에 참조하도록 설계되어 병렬 처리와 장기 의존성 학습에 모두 유리합니다.
FP32, FP16, BF16, FP8은 각각 무엇인가요?
  • 모델의 가중치와 연산에 사용하는 부동소수점의 정밀도, 즉 숫자 하나를 몇 비트로 표현할지를 나타냅니다.
  • FP32: 32비트 단정밀도로 오랫동안 기본값이었습니다. 파라미터 하나당 4바이트를 씁니다.
  • FP16: 16비트로 메모리가 절반이고, GPU의 전용 연산 유닛을 활용해 훨씬 빠릅니다. 다만 표현 범위가 좁아 값이 너무 작아지면 0으로 뭉개지는 문제가 있습니다.
  • BF16: 같은 16비트지만 지수부를 FP32와 동일하게 유지하고 가수부를 줄인 형식입니다. 정밀도는 FP16보다 낮아도 표현 범위가 넓어 학습이 안정적이라 최근 학습에 많이 쓰입니다.
  • FP8: 8비트로 파라미터당 1바이트만 사용합니다. 최신 GPU에서 지원하며 메모리와 대역폭을 크게 아끼지만, 정밀도가 매우 낮아 별도의 스케일링 기법이 필요합니다.
낮은 정밀도를 사용하는 이유는 무엇인가요?
  • LLM은 파라미터가 많아 가중치를 올릴 메모리 자체가 병목이 됩니다. 정밀도를 절반으로 낮추면 필요한 메모리도 절반이 됩니다.
  • 예를 들어 70억 파라미터 모델은 FP32에서 약 28GB가 필요하지만 FP16에서는 약 14GB로 줄어, 더 작은 GPU에도 올릴 수 있습니다.
  • 메모리에서 읽어오는 데이터량이 줄어 추론 속도도 함께 빨라집니다.
  • 학습에서는 연산은 FP16이나 BF16으로 하되 가중치 갱신은 FP32로 유지하는 혼합 정밀도(Mixed Precision) 방식을 주로 사용해, 속도와 안정성을 함께 확보합니다.
  • 추론에서는 여기서 더 나아가 INT8이나 INT4로 양자화하기도 하는데, 정밀도를 낮출수록 메모리는 아끼지만 출력 품질이 떨어질 수 있어 그 사이에서 균형을 잡아야 합니다.

6.3 Transformer와 LLM

Tokenization과 Embedding은 각각 무엇인가요?
  • Tokenization: 텍스트를 모델이 처리할 수 있는 단위(토큰, 예: 단어·서브워드)로 분리하는 과정입니다.
  • Embedding: 각 토큰을 의미를 담은 고정 차원의 벡터로 변환하는 과정으로, 의미가 비슷한 토큰일수록 벡터 공간에서 가깝게 위치합니다.
Self-Attention은 어떻게 동작하나요?
  • 시퀀스 내의 각 토큰이 자신을 포함한 다른 모든 토큰과의 관련도(가중치)를 계산해, 그 가중치로 다른 토큰들의 정보를 가중합해 자신의 표현을 갱신하는 메커니즘입니다.
  • 이를 통해 멀리 떨어진 토큰 간의 관계도 한 번의 연산으로 직접 반영할 수 있습니다.
Query, Key, Value는 각각 어떤 역할을 하나요?
  • Query: 현재 토큰이 "무엇을 찾고 있는지"를 나타내는 벡터입니다.
  • Key: 각 토큰이 "자신이 어떤 정보를 갖고 있는지"를 나타내는 벡터로, Query와의 유사도를 계산하는 데 사용됩니다.
  • Value: 실제로 전달할 정보 벡터로, Query-Key 유사도(Attention 가중치)로 Value들을 가중합해 최종 출력을 만듭니다.
Multi-head Attention은 왜 필요한가요?
  • Self-Attention을 여러 개의 독립된 헤드로 병렬 수행해, 각 헤드가 서로 다른 관점(문법적 관계, 의미적 관계 등)의 패턴을 학습할 수 있게 합니다.
  • 하나의 Attention만 쓰는 것보다 더 풍부한 표현을 학습할 수 있습니다.
Positional Encoding은 왜 필요한가요?
  • Self-Attention은 토큰을 순서와 무관하게 동시에 처리하기 때문에, 토큰의 위치(순서) 정보를 자체적으로 알지 못합니다.
  • Positional Encoding으로 각 토큰의 위치 정보를 임베딩에 더해줌으로써, 모델이 순서를 구분할 수 있게 합니다.
Transformer의 Encoder와 Decoder는 어떻게 다른가요?
  • Encoder: 입력 시퀀스 전체를 양방향으로 참조해 문맥을 압축한 표현을 만듭니다. (BERT 계열)
  • Decoder: 이전에 생성된 토큰까지만 참조할 수 있도록 마스킹된(단방향) Self-Attention을 사용해 다음 토큰을 순차적으로 생성합니다. (GPT 계열)
  • 번역처럼 Encoder-Decoder를 함께 쓰는 구조도 있고, 대부분의 최신 LLM은 Decoder만 사용하는 구조입니다.
LLM은 어떻게 텍스트를 생성하나요? (Autoregressive Generation)
  • 지금까지 생성된 토큰들을 입력으로 받아, 다음에 올 토큰의 확률 분포를 예측하고 그 중 하나를 선택해 이어 붙이는 과정을 반복합니다.
  • 즉, 한 번에 전체 문장을 생성하는 것이 아니라 토큰을 한 개씩 순차적으로(자기회귀적으로) 생성합니다.
Context Window란 무엇인가요?
  • 모델이 한 번에 참조할 수 있는 입력(과 생성될 출력)의 최대 토큰 길이입니다.
  • Context Window를 넘어서는 과거 대화나 문서는 모델이 참조할 수 없기 때문에, 긴 문서를 다룰 때는 청킹이나 요약, RAG 같은 보완 전략이 필요합니다.
Temperature, Top-k, Top-p는 각각 무엇인가요?
  • Temperature: 다음 토큰을 뽑을 확률 분포를 얼마나 뾰족하게(확신 있게) 또는 평평하게(다양하게) 만들지 조절합니다. 값이 낮을수록 결정적, 높을수록 다양하고 창의적인 출력이 됩니다.
  • Top-k: 확률이 높은 상위 k개의 토큰만 후보로 남기고 그중에서 샘플링합니다.
  • Top-p(Nucleus Sampling): 누적 확률이 p가 될 때까지의 토큰들만 후보로 남기고 샘플링해, 후보 수를 상황에 맞게 동적으로 조절합니다.

6.4 RAG

RAG란 무엇이며 왜 사용하나요?
  • Retrieval-Augmented Generation, 질문과 관련된 외부 문서를 검색해 LLM의 입력(프롬프트)에 함께 넣어준 뒤 답변을 생성하게 하는 방식입니다.
  • LLM을 재학습하지 않고도 최신 정보나 내부 문서 기반 답변이 가능해지고, 근거 문서를 함께 제시해 Hallucination을 줄이는 데 도움이 됩니다.
Document Chunking은 왜 필요한가요?
  • 긴 문서를 그대로 검색·임베딩에 사용하면 Context Window 제한에 걸리거나, 문서 전체 중 실제로 관련 있는 부분만 정확히 찾기 어렵습니다.
  • 문서를 적절한 크기의 조각(Chunk)으로 나누면 검색 정확도를 높이고, 필요한 부분만 프롬프트에 담을 수 있습니다.
Embedding Model과 Vector Search는 어떻게 동작하나요?
  • Embedding Model이 텍스트(질문, 문서 청크)를 의미를 담은 벡터로 변환합니다.
  • Vector Search는 질문 벡터와 문서 벡터 간의 거리(유사도, 예: 코사인 유사도)를 계산해, 의미적으로 가장 가까운 문서들을 찾아냅니다.
Dense Retrieval과 Sparse Retrieval의 차이는 무엇인가요?
  • Sparse Retrieval: 단어의 등장 여부·빈도 기반으로 검색합니다. (예: TF-IDF, BM25) 키워드가 정확히 일치할 때 강합니다.
  • Dense Retrieval: 임베딩 벡터 간 유사도로 검색합니다. 단어가 정확히 일치하지 않아도 의미가 비슷하면 찾아낼 수 있습니다.
Hybrid Search는 무엇인가요?
  • Sparse Retrieval(키워드 매칭)과 Dense Retrieval(의미 기반 검색)의 결과를 함께 사용하는 검색 방식입니다.
  • 키워드 정확도와 의미적 유연성을 모두 보완해, 한 가지 방식만 쓸 때보다 검색 품질이 높아집니다.
Reranking은 왜 필요한가요?
  • 1차 검색(Retrieval)은 속도를 위해 비교적 가벼운 방식으로 넓게 후보를 가져오는데, 이 후보들의 순서가 실제 관련도와 정확히 일치하지 않을 수 있습니다.
  • Reranking은 더 정교한(비용이 큰) 모델로 후보들을 다시 채점·재정렬해, 최종적으로 가장 관련성 높은 문서를 상위에 배치합니다.
Query Transformation이란 무엇인가요?
  • 사용자의 원래 질문을 검색에 더 유리한 형태로 변형·보강하는 과정입니다. (예: 질문 재작성, 하위 질문으로 분해, 키워드 확장)
  • 사용자의 질문이 모호하거나 검색 문서와 표현이 다를 때 검색 성능을 높이는 데 사용됩니다.
Metadata Filtering은 RAG에서 어떻게 활용되나요?
  • 문서에 날짜, 카테고리, 작성자 같은 메타데이터를 함께 저장해 두고, 벡터 유사도 검색 전후에 조건에 맞는 문서로 범위를 좁히는 방식입니다.
  • 예를 들어 "최근 1년 내 공지사항만" 같은 조건을 벡터 검색과 함께 적용해 검색 정확도를 높일 수 있습니다.
Context Precision과 Recall은 무엇인가요?
  • Context Precision: 검색된 문서 중 실제로 답변에 유용한(관련 있는) 문서의 비율입니다.
  • Context Recall: 정답을 만들기 위해 필요한 관련 문서 중 실제로 검색된 문서의 비율입니다.
  • 두 지표로 RAG의 검색 단계가 얼마나 정확하고 얼마나 빠짐없이 관련 문서를 가져오는지를 평가합니다.
Faithfulness와 Hallucination은 무엇인가요?
  • Faithfulness(충실성): 모델의 답변이 실제로 제공된 근거 문서(Context)에 기반하고 있는 정도입니다.
  • Hallucination(환각): 모델이 근거 문서에 없는 내용을, 또는 사실이 아닌 내용을 그럴듯하게 만들어내는 현상입니다.
  • RAG는 근거 문서를 제시해 Hallucination을 줄이는 데 도움을 주지만, 검색된 문서를 무시하고 잘못된 내용을 생성하는 경우도 있어 Faithfulness 평가가 별도로 필요합니다.

6.5 AI Agent

AI Agent와 일반 LLM 호출은 어떻게 다른가요?
  • 일반 LLM 호출은 입력 프롬프트에 대해 한 번의 응답을 생성하는 단발성 호출입니다.
  • AI Agent는 목표가 주어지면 스스로 계획을 세우고, 필요하면 도구(Tool)를 호출하고, 결과를 관찰해 다음 행동을 결정하는 과정을 여러 번 반복하며 목표를 달성해 나가는 시스템입니다.
Tool Calling이란 무엇인가요?
  • LLM이 스스로 답할 수 없는 작업(최신 정보 조회, 계산, DB 조회, 외부 API 호출 등)이 필요할 때, 미리 정의된 함수(도구)를 어떤 파라미터로 호출할지 스스로 판단해 요청하는 기능입니다.
  • LLM이 직접 도구를 실행하는 것이 아니라, 호출할 도구와 파라미터를 제안하면 애플리케이션이 실제로 실행하고 결과를 다시 LLM에 전달합니다.
Agent의 Planning은 무엇인가요?
  • 주어진 목표를 달성하기 위해 필요한 하위 작업들을 순서대로 나누고 계획을 세우는 과정입니다.
  • 복잡한 작업을 한 번에 처리하지 않고 단계별로 나눠 수행함으로써 성공률과 결과의 품질을 높일 수 있습니다.
Reflection은 왜 필요한가요?
  • Agent가 자신이 수행한 결과(또는 중간 과정)를 스스로 평가하고 부족한 부분을 찾아 수정하는 과정입니다.
  • 첫 시도에서 완벽한 결과를 내지 못해도, 스스로 검토하고 개선하는 루프를 거쳐 최종 결과물의 품질을 높일 수 있습니다.
Agent의 Short-term Memory와 Long-term Memory는 각각 무엇인가요?
  • Short-term Memory: 현재 진행 중인 작업의 맥락(대화 기록, 최근 관찰 결과)을 유지하는 메모리로, 보통 Context Window 안에서 관리됩니다.
  • Long-term Memory: 세션이 끝나도 유지되어야 할 정보(사용자 선호, 과거 경험 등)를 별도 저장소(주로 벡터 DB)에 저장해 두고 필요할 때 검색해 불러오는 메모리입니다.
Workflow와 Agent의 차이는 무엇인가요?
  • Workflow: 사람이 미리 정해둔 고정된 순서(단계)를 따라 LLM 호출과 로직이 실행되는 방식입니다. 동작이 예측 가능하고 안정적입니다.
  • Agent: 정해진 순서 없이 LLM이 상황에 따라 스스로 다음 행동(도구 호출, 계획 수정 등)을 결정합니다. 더 유연하지만 결과 예측이 어렵고 제어가 까다롭습니다.
Multi-agent 구조는 무엇인가요?
  • 하나의 거대한 Agent 대신, 역할이 나뉜 여러 개의 Agent(예: 계획자, 실행자, 검토자)가 서로 협력하거나 순차적으로 작업을 이어받아 처리하는 구조입니다.
  • 각 Agent가 좁은 역할에 집중할 수 있어 복잡한 작업을 더 안정적으로 분업할 수 있지만, Agent 간 통신·조율 비용과 복잡도가 늘어납니다.

6.6 LLM 활용 전략

Prompt Engineering, RAG, Fine-tuning은 어떤 기준으로 선택하나요?
  • Prompt Engineering: 가장 빠르고 저렴합니다. 모델이 이미 알고 있는 것을 잘 끌어내거나 출력 형식을 다듬는 데 적합합니다.
  • RAG: 모델이 학습하지 못한 최신 정보나 사내 문서를 다뤄야 할 때 사용합니다. 데이터가 바뀌면 문서만 갱신하면 되어 유지보수가 쉽습니다.
  • Fine-tuning: 특정한 말투, 출력 형식, 도메인 특유의 판단 패턴을 일관되게 재현해야 할 때 사용합니다.
  • 흔한 오해는 지식을 주입하려고 파인튜닝을 시도하는 것입니다. 지식 문제는 대체로 RAG가 맞고, 파인튜닝은 지식보다 행동 방식을 학습시키는 데 적합합니다.
  • 실무에서는 프롬프트로 먼저 시도하고, 부족하면 RAG를, 그래도 부족할 때 파인튜닝을 검토하는 순서가 일반적입니다.
Few-shot과 Chain-of-Thought는 무엇인가요?
  • Few-shot: 원하는 입력과 출력의 예시를 프롬프트에 몇 개 넣어, 모델이 그 패턴을 따르게 하는 방법입니다. 출력 형식을 맞출 때 특히 효과적입니다.
  • Chain-of-Thought: 바로 답하지 말고 풀이 과정을 단계별로 서술하게 유도하는 방법으로, 여러 단계를 거쳐야 하는 추론 문제의 정확도를 높입니다.
  • 다만 출력이 길어져 비용과 응답 시간이 늘어나고, 추론에 특화된 최신 모델은 이 과정을 내부적으로 수행하기도 합니다.
LoRA는 무엇이며 왜 사용하나요?
  • 기존 가중치는 그대로 얼려두고, 작은 크기의 행렬만 추가로 붙여 그 부분만 학습하는 방식입니다.
  • 학습해야 할 파라미터 수가 크게 줄어 훨씬 적은 GPU 메모리로 파인튜닝할 수 있습니다.
  • 결과물도 작아서 하나의 기반 모델에 용도별 어댑터를 갈아 끼우는 형태로 운영할 수 있습니다.
  • 기반 모델을 양자화해 올린 뒤 LoRA를 적용하는 QLoRA를 쓰면 자원 요구량을 더 줄일 수 있습니다.
임베딩 간 유사도는 어떻게 측정하나요?
  • 코사인 유사도가 가장 널리 쓰입니다. 두 벡터가 이루는 각도만 보기 때문에, 벡터의 크기 차이에 영향을 받지 않고 의미의 방향만 비교합니다.
  • 문서 길이에 따라 벡터 크기가 달라질 수 있어, 크기를 배제하는 이 성질이 텍스트 검색에 유리합니다.
  • 벡터를 미리 정규화해 두면 코사인 유사도는 내적과 같아지므로, 검색 시 연산을 더 빠르게 할 수 있습니다.
벡터 데이터베이스는 왜 필요한가요?
  • 임베딩 벡터를 저장하고 유사한 벡터를 빠르게 찾기 위한 저장소입니다.
  • 모든 벡터와 일일이 비교하는 정확한 탐색은 데이터가 많아지면 감당할 수 없으므로, 근사 최근접 탐색 알고리즘으로 정확도를 조금 양보하고 속도를 확보합니다.
  • 규모가 크지 않다면 PostgreSQL의 pgvector처럼 기존 DB의 확장을 쓰는 편이 운영 부담이 적고, 데이터와 벡터를 한 트랜잭션에서 다룰 수 있다는 장점도 있습니다.
LLM 애플리케이션은 어떻게 평가하나요?
  • 정답이 하나로 정해지지 않아 기존의 정확도 지표만으로는 판단하기 어렵습니다.
  • 대표 질문과 기대 답변으로 평가셋을 만들어 두고, 프롬프트나 모델을 바꿀 때마다 같은 셋으로 비교해 성능이 나빠지지 않았는지 확인합니다.
  • 채점은 사람이 직접 하거나, 다른 LLM에게 기준을 주고 평가하게 하는 방식을 함께 사용합니다.
  • RAG라면 검색이 관련 문서를 잘 가져왔는지와 생성이 그 문서에 충실했는지를 나누어 평가해야, 문제가 어느 단계에서 발생했는지 알 수 있습니다.
LLM 서비스의 응답 지연과 비용은 어떻게 줄이나요?
  • 매번 반복되는 긴 시스템 프롬프트나 문서는 프롬프트 캐싱을 적용해 입력 토큰 비용과 지연을 줄입니다.
  • 응답을 스트리밍으로 전달하면 전체 생성 시간은 같아도 사용자가 느끼는 대기 시간이 크게 줄어듭니다.
  • 모든 요청에 가장 큰 모델을 쓰지 말고, 단순한 작업은 작은 모델로 라우팅합니다.
  • 컨텍스트에 불필요한 내용을 넣지 않고 출력 길이를 제한하며, 동일한 요청이 반복된다면 결과 자체를 캐싱합니다.

7. 시스템 설계

7.1 아키텍처

모놀리식과 마이크로서비스의 차이는 무엇인가요?
  • 모놀리식: 하나의 애플리케이션에 모든 기능이 들어 있습니다. 개발과 배포가 단순하고, 트랜잭션으로 데이터 정합성을 지키기 쉽습니다. 대신 규모가 커지면 빌드와 배포가 느려지고 작은 변경도 전체 재배포가 필요합니다.
  • 마이크로서비스: 기능별로 독립된 서비스로 나눕니다. 서비스마다 따로 배포하고 확장할 수 있으며 장애도 격리됩니다. 대신 서비스 간 통신, 분산 트랜잭션, 배포와 모니터링 등 운영 복잡도가 크게 늘어납니다.
  • 처음부터 마이크로서비스로 시작하는 것은 대개 과합니다. 도메인 경계가 명확해지고 팀이 커져 배포 충돌이 잦아질 때 분리를 검토하는 것이 일반적입니다.
계층형 아키텍처는 왜 사용하나요?
  • 표현(Controller), 비즈니스(Service), 영속성(Repository) 계층으로 나누어 각 계층이 아래 계층에만 의존하게 만듭니다.
  • 관심사가 분리되어 변경의 영향 범위가 좁아지고, 계층별로 테스트하기 쉬워집니다.
  • 주의할 점은 모든 로직이 Service에 절차적으로 몰리는 경우입니다. 엔티티가 데이터만 담고 아무 행위도 하지 않게 되면 객체지향의 이점을 잃게 되므로, 도메인 규칙은 엔티티 안에 두는 편이 좋습니다.
서비스 간 통신은 동기와 비동기 중 무엇을 선택해야 하나요?
  • 동기 통신: 요청하고 응답을 기다립니다. 흐름이 직관적이지만 호출 대상이 느려지거나 죽으면 그 영향이 그대로 전파됩니다.
  • 비동기 통신: 메시지를 발행하고 바로 다음으로 넘어갑니다. 장애가 격리되고 부하를 완충할 수 있지만, 결과가 즉시 반영되지 않고 중복과 순서를 고려해야 합니다.
  • 사용자에게 즉시 응답해야 하는 것은 동기로, 알림 발송이나 통계 집계처럼 조금 늦어도 되는 부가 작업은 비동기로 처리하는 것이 일반적인 기준입니다.

7.2 분산 시스템

CAP 정리란 무엇인가요?
  • 분산 시스템은 일관성(Consistency), 가용성(Availability), 분단 내성(Partition Tolerance)을 동시에 모두 만족할 수 없다는 이론입니다.
  • 네트워크 장애는 현실에서 피할 수 없으므로 분단 내성은 사실상 전제이고, 결국 분단이 발생했을 때 일관성과 가용성 중 무엇을 포기할지의 선택이 됩니다.
  • 잔고처럼 틀린 값을 보여주면 안 되는 경우는 일관성을 택해 응답을 거부하고, 피드나 추천처럼 잠시 오래된 값이어도 괜찮은 경우는 가용성을 택합니다.
여러 서비스에 걸친 트랜잭션은 어떻게 처리하나요?
  • 서비스마다 DB가 분리되어 있으면 하나의 트랜잭션으로 묶을 수 없습니다.
  • 2단계 커밋은 강한 일관성을 주지만, 조정자가 장애를 겪으면 참여자들이 계속 대기하게 되고 성능도 떨어져 실무에서는 잘 쓰이지 않습니다.
  • 대신 Saga 패턴을 사용합니다. 각 서비스가 자신의 로컬 트랜잭션을 순서대로 수행하고, 중간에 실패하면 이미 수행한 작업을 되돌리는 보상 트랜잭션을 실행합니다.
  • 되돌리는 방식이므로 중간 상태가 잠시 외부에 보일 수 있고, 보상 로직 자체가 실패하는 경우까지 설계해야 한다는 부담이 있습니다.
메시지 중복 처리는 왜 발생하며 어떻게 대응하나요?
  • 대부분의 메시지 시스템은 최소 한 번 전달을 보장합니다. 즉 유실은 막지만 중복은 발생할 수 있습니다.
  • 소비자가 처리를 끝내고 응답을 보내기 직전에 장애가 나면, 시스템은 처리되지 않았다고 판단해 같은 메시지를 다시 전달합니다.
  • 대응은 소비자를 멱등하게 만드는 것입니다. 메시지마다 고유 ID를 두고 이미 처리한 것인지 기록해 확인하거나, 여러 번 실행해도 결과가 같도록 로직을 설계합니다.
  • 정확히 한 번 전달은 비용이 매우 크기 때문에, 최소 한 번 전달에 멱등한 소비자를 조합하는 것이 현실적인 표준입니다.
서킷 브레이커는 무엇이며 왜 필요한가요?
  • 외부 서비스 호출이 반복적으로 실패하면 일정 시간 동안 호출 자체를 차단하고 즉시 실패로 처리하는 패턴입니다.
  • 응답하지 않는 서비스를 계속 호출하면 스레드와 커넥션이 대기 상태로 묶여, 결국 호출하는 쪽까지 함께 죽는 연쇄 장애가 발생합니다. 이를 끊기 위한 장치입니다.
  • 정상 상태에서 실패가 임계치를 넘으면 차단 상태로 바뀌고, 일정 시간 뒤 일부 요청만 시험적으로 보내 회복되었는지 확인한 후 정상으로 돌아갑니다.
  • 타임아웃 설정, 지수 백오프 재시도, 실패 시 대체 응답을 주는 폴백과 함께 사용해야 효과가 있습니다.
분산 환경에서 동시성 제어는 어떻게 하나요?
  • 서버가 여러 대이면 언어 수준의 락은 각 프로세스 안에서만 유효하므로 아무 소용이 없습니다.
  • 가장 확실한 방법은 모두가 공유하는 DB를 활용하는 것입니다. 비관적 락으로 행을 잠그거나, 유니크 제약으로 중복 자체를 DB가 막게 합니다.
  • Redis 기반 분산 락도 널리 쓰이지만 만료와 해제 조건을 정확히 다뤄야 하며, 완벽한 상호 배제를 보장하지는 않습니다.
  • 가능하다면 락을 쓰기보다 단일 UPDATE 문처럼 DB가 원자적으로 처리하도록 만들거나, 애초에 멱등하게 설계해 동시 실행되어도 문제가 없게 하는 편이 더 안전합니다.

7.3 운영

대용량 트래픽에는 어떻게 대응하나요?
  • 먼저 어디가 병목인지 측정해야 합니다. 추측으로 최적화하면 효과 없는 곳에 노력을 쓰게 됩니다.
  • 반복 조회는 캐시로 걷어내고, 읽기 부하는 읽기 전용 복제본으로 분산하며, 정적 자원은 CDN으로 넘깁니다.
  • 서버를 무상태로 만들어 로드 밸런서 뒤에서 수평 확장할 수 있게 합니다.
  • 즉시 응답할 필요가 없는 무거운 작업은 큐로 넘겨 비동기 처리합니다.
  • 결국 DB가 마지막 병목이 되므로 인덱스와 쿼리 개선을 먼저 하고, 그래도 부족할 때 샤딩처럼 구조를 바꾸는 방법을 검토합니다.
장애를 어떻게 감지하고 원인을 추적하나요?
  • 지표, 로그, 추적 세 가지를 함께 봅니다. 지표로 이상을 감지하고, 추적으로 어느 구간이 느린지 좁히고, 로그로 구체적인 원인을 확인합니다.
  • 여러 서비스를 거치는 요청은 공통 요청 ID를 부여해야 전체 흐름을 하나로 이어서 볼 수 있습니다.
  • 응답 시간은 평균이 아니라 상위 백분위로 봐야 합니다. 평균은 정상이어도 일부 사용자는 심각한 지연을 겪고 있을 수 있습니다.
  • 알림은 에러율과 지연처럼 사용자 경험에 직결되는 지표를 기준으로 걸어야 실제로 대응이 필요한 상황만 걸러집니다.
무중단 배포는 어떻게 하나요?
  • 롤링: 인스턴스를 조금씩 순차적으로 교체합니다. 추가 자원이 적게 들지만 배포 중에는 두 버전이 함께 동작합니다.
  • 블루/그린: 새 버전 환경을 통째로 띄운 뒤 트래픽을 한 번에 전환합니다. 문제가 생기면 즉시 되돌릴 수 있지만 자원이 두 배로 필요합니다.
  • 카나리: 일부 트래픽만 새 버전으로 보내 지표를 확인하며 점진적으로 확대합니다.
  • 어떤 방식이든 배포 중에는 두 버전이 공존하므로, DB 스키마 변경은 하위 호환되도록 나누어 적용해야 합니다. 컬럼을 먼저 추가하고, 새 코드를 배포하고, 사용하지 않게 된 뒤에 제거하는 순서입니다.