[CS] 면접 예상 질문 목록

성승모·2025년 10월 13일

CS

목록 보기
1/5

프로그래밍

절차 지향 vs 객체 지향 vs 함수형 프로그래밍

  1. 절차지향
    • 프로그램을 절차(순서) 중심으로 구성하는 방식
    • 코드의 실행 순서가 중요
    • 함수 중심 설계
       → C, Pascal
  2. 객체 지향
    • 현실 세계의 개념을 추상화하여 객체로 표현
    • 데이터(속성) 와 행동(메서드) 을 하나의 객체로 묶어 관리하는 방식
       → Java, C++
  3. 함수형 프로그래밍
    • 상태 변경을 피하고, 불변성과 순수 함수를 강조
    • 함수를 1급 시민(First-class citizen) 으로 다루는 패러다임
       → Kotlin, Scala

SOLID

  1. SRP: 단일 책임 원칙
    : 하나의 모듈은 하나의 액터에 대해서만 책임져야 한다.
  2. OCP: 개방-폐쇄 원칙
    : 확장엔 열려있고 변경에는 닫혀 있어야 한다.
  3. LSP: 리스코프 치환 원칙
    : o1: S와 o2: T가 있고, T 타입을 이용해서 정의한 프로그램 P에서 o2의 자리에 o1을 치환하더라도 P의 행위가 변하지 않는다면 S는 T의 하위타입이다.
  4. ISP: 인터페이스 분리 원칙
    : 서로 관련없는 인터페이스는 서로 분리하여 관리한다.
  5. DIP: 의존성 역전 법칙
    : 제어와 의존 방향을 바꾸어 구체적인 것들로부터 추상적인 것을 분리하여 관리

Call by Value와 Call by Reference

개념

  1. Call by Value
    • 인자의 값(Value) 을 복사하여 함수에 전달
    • 독립적인 복사본 존재
  2. Call by Reference
    • 인자의 주소(Address) 를 전달
    • 동일한 객체/데이터 참조

Kotlin

 Kotlin은 기본적으로 Call by Value이다. 이는 Int, Boolean 같은 Jvm이 primitives 타입을 사용하는 클래스에 해당한다. 반면에, 우리가 직접 생성하는 class를 사용할 때는 주의해야 한다. Kotlin은 이를 Call By Value of Reference 형태로 가져오며, 이는 variable인 class 내 프로퍼티를 변경을 가능하게 한다. 하지만 새로 할당하는 것은 불가능하다.

data class Person(var name: String)

fun changeName(p: Person) {
    p.name = "Alice"
}
fun newPerson(p: Person) {
    p = Person("Alice")
}

fun main() {
    val person = Person("Bob")
    changeName(person)
    println(person.name)	// Alice
    
    val person2 = Person("Jake")
    newPerson(person2)
    println(person2.name)	// Jake
}

따라서, 불변성을 유지하기 위해서는 class 내 프로퍼티를 value로 정의하고, 변경 시 객체 자체를 변경하는 것이 좋다.

추상 클래스 vs 인터페이스

 추상 클래스와 인터페이스 둘 다 추상화 및 상속을 구현할 수 있다. 추상 클래스는 기본적인 기능을 미리 정의해놓을 수 있다. 즉, 구현부와 추상부가 동시에 존재하고 이를 활용하려면 상속하여 추상부를 구체화 해주어야 한다. 반면에 인터페이스는 추상부만 존재하며 구현부가 없다. 따라서, 둘의 차이는 기본 기능 즉, 구현체의 유무이며 추상 클래스는 프로퍼티, 생성자도 가질 수 있다는 것이 큰 차이이다.

운영 체제

메모리 종류

  1. RAM (Random Access Memory)
    : 전원이 꺼지면 데이터가 지워지는 휘발성이며, 현재 실행 중인 프로그램이나 데이터를 임시로 저장하는 주된 메모리이다.

  2. ROM (Read Only Memory)
    : 전원이 꺼져도 데이터가 삭제되지 않으며, 주로 컴퓨터의 부팅 프로그램 등 하드웨어 초기화에 필요한 중요한 데이터를 저장하고 수정이 어렵다.

  3. Flash Memory
    : 비휘발성 메모리이며, 필요에 따라 데이터를 쓰고 지울 수 있다.

  4. 레지스터
    : CPU 내부에 있는 가장 빠른 메모리로, 연산에 필요한 데이터를 일시적으로 저장하여 성능을 높인다.

  5. 캐시 메모리
    : CPU와 주기억 장치 사이에 위치한 SRAM이며, 둘의 성능 차이를 보완하여 성능을 높인다.

RAM 영역

  • Code
    : 실행할 프로그램의 명령어(기계어 코드)가 저장되는 영역으로 주로 메서드의 코드가 저장된다.
  • Data
    : 전역 변수, static 변수 등 프로그램 전체에서 공유되는 데이터가 저장된다.
  • Heap
    : 개발자가 직접 new, malloc 등으로 동적으로 생성한 객체들이 저장되며 GC가 자동으로 관리한다. 하지만 순환 참조 등에 의해 메모리 누수가 발생할 수 있으니 유의해서 관리해야 한다.
  • Stack
    : 함수 호출 시 생성되는 지역 변수, 매개변수 등을 저장하며 LIFO로 작동한다. 관리가 빠르고 안전하지만 개발자에 의한 동적 크기 조절이 어렵다.

프로세스 vs 쓰레드

프로세스는 실행 중인 프로그램의 객체이며, 독립된 메모리 영역과 높은 비용을 요구한다. 쓰레드는 프로세스 내에서 실행되는 최소 단위로 공유 메모리로 쓰레드 간 통신이 쉬우며 비교적 적은 성능을 요구한다. 하지만 상호 공유가 쉬다는 것과 동시에 한 쓰레드에서의 오류가 전체에 치명적일 수도 있다는 주의점도 있다. 메모리 영역에서 살펴보면 쓰레드 간에는 Code/Data/Heap 영역을 공유하고 별도의 Stack을 가진다.

IPC: 프로세스 간 통신

 프로세스는 서로 독립된 메모리를 갖기 때문에 서로 데이터를 주고 받을 수 없다. 따라서 OS가 제공하는 IPC 메커니즘을 이용해야 한다. 해당 메커니즘은 다음과 같은 것들이 있다.

  • 파이프: 한쪽 프로세스가 데이터를 쓰면 다른 쪽이 읽고 단방향이다.
  • 이름 있는 파이프: 파이프에 이름을 붙여 분리할 수 있다.
  • 메시지 큐: 큐를 통해 메시지를 송수신
  • 공유 메모리: 공유 메모리 구역을 만들어 활용한다.
  • 소켓: 네트워크 기반 통신
  • 시그널: 이벤트로 알린다.

컨텍스트 스위칭

 프로세스나 스레드의 상태를 의미하는 컨텍스트를 잠시 멈추고 다른 컨텍스트로 전환하는 것을 뜻한다. 여기서 상태는 CPU, 메모리의 레지스터, PC, 스택 포인터 등을 의미한다. 따라서, 스위칭한 뒤엔 레지스터 저장/복원, 캐시 미스 증가, 커널 모드 전환 등 상태를 복원하는 과정이 필요하기 때문에 비용이 발생한다.

커널이란?
 운영체제 중 항상 메모리에 올라가 있는 운영체제의 핵심 부분으로써 하드웨어와 응용 프로그램 사이에서 인터페이스를 제공하는 역할을 하며 컴퓨터 자원들을 관리하는 역할을 한다. 즉, 하드웨어를 제어하기 위해 응용 프로그램에 제공되는 인터페이스이다.

 보통 OS의 커널에 의해서 수행되며 주어진 Time Slice 소진, 리소스 요구, 인터럽트 등에 의해 발생한다. 종류는 프로세스 to 프로세스(P2P)와 쓰레드 to 쓰레드(T2T) 두 가지가 있다. 둘 모두 커널 모드에서 실행되고 CPU의 레지스터 상태를 교체한다. 이 때, 쓰레드는 네가지 메모리 영역 중 STACK만 독립적으로 갖기 때문에 이 곳만 상태를 복구하면 된다. 그 말은 즉, P2P의 경우엔 MMU와 TLB도 관리해야 한다는 뜻이다. 또한 다른 작업을 하다 돌아온 것이기 때문에 캐시 메모리가 오염되어있을 가능성이 있다.

데드락 & 임계 영역

데드락은 두 개 이상의 프로세스(또는 스레드)가 서로 자원을 점유한 채, 상대방의 자원을 기다리며 무한 대기 상태에 빠지는 현상이다. 상호 배제, 점유 대기, 비선점, 순환 대기가 조건이며, 네 가지 중 하나라도 깨뜨리면 예방이 가능하다.

  • 상호배제: 한 번에 하나의 프로세스만 특정 자원에 접근할 수 있는 조건.
    → 자원을 공유 가능하게 설계하거나, 병렬 접근이 가능하도록 임계 구역 최소화
  • 점유 대기: 원을 가진 프로세스가 다른 자원을 기다리는 상태.
    → 프로세스 시작 전 필요한 모든 자원을 한 번에 요청하게 하거나, 점유 중엔 추가 요청 금지.
  • 비선점: 이미 할당된 자원을 강제로 회수할 수 없는 조건.
    → 선점 가능한 자원으로 설계하거나, 일정 시간이 지나면 자원을 회수하도록 타임아웃 설정
  • 순환 대기: 프로세스들이 자원을 서로 순환적으로 기다리는 상태
    → 자원에 우선순위(번호)를 부여하여 순서대로만 요청하게 함.

임계 영역은 두 개 이상의 스레드가 동시에 접근하면 문제가 생기는 코드 영역을 뜻하며, 읽기 과정에서 다른 스레드가 끼어들면 예상치 못한 값을 얻을 수 있다.

스케줄러의 종류와 역할

스케줄러는 CPU, IO 등 시스템 자원을 어떤 프로세스(또는 스레드)에 언제 할당할지 결정하는 OS 구성 요소이다. 운영체제 관점에서 크게 3가지 스케줄러가 존재하다.

  1. 장기 스케줄러: 프로세스 생성 시 메모리에 진입할 프로세스를 결정한다.
  2. 중기 스케줄러: 메모리에서 프로세스를 일시 중지하거나 재개한다.
         → 멀티태스킹 조절
  3. 단기 스케줄러: CPU를 어떤 프로세스에 할당한지 결정한다.
         → 가장 빈번하게 호출

단기 스케줄러가 CPU 프로세스를 선택하는 방법은 두 가지가 있다.

  1. 선점(Preemptive) 스케줄링
    • CPU 사용 중인 프로세스를 강제로 중단하고 다른 프로세스를 할당
    • Round Robin (RR), Shortest Remaining Time First (SRTF)
  2. 비선점(Non-Preemptive) 스케줄링
    • 한 프로세스가 CPU를 점유하면 끝날 때까지 다른 프로세스가 CPU를 가져갈 수 없음
    • First-Come-First-Served (FCFS), Shortest Job First (SJF)

Android는 Linux 커널에서 Completely Fair Scheduler (CFS)
1️⃣ 프로세스 준비
: 실행 가능한 프로세스들은 모두 RB-Tree에 삽입되고 각자 vruntime 값을 가집니다.
2️⃣ 스케줄링 결정
: 가장 낮은 vruntime를 갖는 작업 실행
3️⃣ CPU 실행 및 vruntime 업데이트
: 실행 시간만큼 vruntime이 증가, CPU 사용량이 많으면 vruntime이 빨리 증가 → 다음에 덜 실행됨

페이징

 운영체제가 메모리를 일정한 크기의 블록 단위로 관리하는 기법으로 프로세스의 논리적 주소와 물리적 메모리를 페이지프레임으로 나누어 관리한다. 이를 통해 비연속적인 메모리 공간에 프로레스를 배치하고 단편화를 문제를 해결할 수 있다.

구분의미예시
페이지(Page)프로세스의 논리 메모리를 일정 크기로 나눈 단위프로세스 코드/데이터 일부
프레임(Frame)실제 물리 메모리(RAM)를 동일 크기로 나눈 단위실제 RAM 내의 블록
페이지 테이블(Page Table)논리 주소와 물리 주소 간의 매핑 정보를 관리Page 0 → Frame 5, Page 1 → Frame 3 등
  • 장점
    → 외부 단편화 해결
    → 프로세스가 비연속적으로 배치 가능
    → 가상 메모리 구현의 기초
  • 단점
    → 내부 단편화 발생
    → 페이지 테이블 관리 비용 증가
    → 주소 변환 오버헤드 증가

TLB(Translation Lookaside Buffer)란?
 페이징을 하더라도 Page Table에 접근, 물리 주소 접근 총 두 번의 접근이 필요하다. TLB는 Page Table 접근 전에 위치하여, 해당 페이지 정보가 TLB에 존재하면 바로 물리적 주소를 제공한다.
 만약, TLB 내에 없다면 Page Table 접근한다. Page Table 엔트리(PTE)가 invalid라면 Page Fault가 발생하고 페이지를 적재한 뒤 Page Table를 갱신한다. valid라면 TLB를 바로 갱신한다.

https://velog.io/@jaeyunn_15/OS-TLB-MMU

가상 메모리

 프로세스의 실제 물리 메모리(RAM) 크기와 상관없이, 자신만의 연속된 메모리 공간을 사용하는 것처럼 보이게 하는 기술이다. 따라서 페이징이 이를 구현하기 위한 핵심 기술이다.
 다음과 같은 원리로 작동한다.

  1. 프로세스는 가상 주소를 사용하여 데이터를 요청
  2. MMU가 페이지 테이블을 참고하여 해당 페이지가 물리 메모리에 있는지 확인
  3. 메모리에 존재하면 → 바로 접근
  4. 메모리에 없으면 → Page Fault 발생

MMU(Memory Management Unit)란?
 CPU와 메모리 사이에 위치한 하드웨어로, CPU가 발생시키는 가상(논리) 주소를 물리 주소로 변환하는 역할을 한다. 페이징이나 세그먼테이션과 같은 메모리 관리 기법을 하드웨어 차원에서 지원하며, 이 과정에서 TLB, 페이지 테이블(또는 세그먼트 테이블) 을 사용해 빨른 주소 변환을 지원한다. 따라서 가상 메모리 시스템을 구현하기 위한 핵심 장치이다.

위 과정에서 Page Fault가 발생하면 운영체제가 디스크에서 페이지를 읽어 메모리에 적재한다. 이것을 교체(Replacement)라고 교체 기준을 정하는 알고리즘도 있다.

  • FIFO (First In First Out) : 가장 오래된 페이지를 교체
  • LRU (Least Recently Used) : 가장 오래 사용되지 않은 페이지를 교체
  • Optimal (OPT) : 앞으로 가장 오랫동안 사용되지 않을 페이지 교체 (이론적 기준)
  • Clock Algorithm : LRU를 근사화한 효율적 구현

자료 구조

주요 자료 구조

자료구조구조접근 방식삽입/삭제시간 복잡도
큐(Queue)선형FIFO삽입: 뒤(enqueue), 삭제: 앞(dequeue)O(1) 삽입/삭제(LinkedList 기준)
스택(Stack)선형LIFO삽입: 뒤(push), 삭제: 뒤(pop)O(1) 삽입/삭제
트리(Tree)계층 구조부모-자식삽입/삭제 위치 규칙 있음O(log n) ~ O(n) (트리 종류에 따라 다름)
힙(Heap)완전 이진 트리부모-자식 관계 유지삽입/삭제: 부모-자식 비교 후 재정렬O(log n) 삽입/삭제, O(1) 최댓값/최솟값 접근

해시 테이블

 키(Key)를 해시 함수(Hash Function)에 넣어, 저장 위치(Index)를 계산하고 값을 저장/검색하는 자료구조로, 평균적으로 탐색, 삽입, 삭제 모두 O(1)의 시간 복잡도를 가진다.
 해시 함수는 매우 큰 범위를 가지지만 충돌 가능성은 여전히 존재한다. 따라서, 충돌을 미리 방지하기 위해 두 가지 방법이 있다. 체이닝(Chaining)는 같은 Index에 연결 리스트를 추가하여 여러 값을 저장하는 방법이다. 구현이 간단하고 크기가 유연하지만 리스트 내부를 모두 순환해야할 수 있다. 오픈 주소법(Open Addressing)은 충돌 시 다른 빈 슬롯을 찾아 저장하는 방법으로, 메모리를 연속적으로 사용하고 캐시 효율이 좋지만 클러스터링(특정 범위 집중 현상)이 발생할 수 있다.

알고리즘

Sorting의 종류와 정의

알고리즘정의/설명시간 복잡도 (평균/최악)특징
버블 정렬(Bubble Sort)인접한 두 원소를 비교해 자리 교환O(n²) / O(n²)구현 간단, 안정 정렬
선택 정렬(Selection Sort)최솟값/최댓값을 찾아 순서대로 위치 교환O(n²) / O(n²)비교적 느림, 불안정
삽입 정렬(Insertion Sort)현재 원소를 정렬된 부분에 삽입O(n²) / O(n²)소규모/거의 정렬된 데이터에 효율적, 안정
퀵 정렬(Quick Sort)Pivot 기준으로 분할 후 재귀 정렬O(n log n) / O(n²)평균 빠름, 불안정
병합 정렬(Merge Sort)리스트를 반씩 나누어 정렬 후 병합O(n log n) / O(n log n)안정, 추가 메모리 필요
힙 정렬(Heap Sort)힙 자료구조를 이용해 최댓값/최솟값 순서대로 추출O(n log n) / O(n log n)불안정, 공간 효율 좋음
계수 정렬(Counting Sort)정수 키 범위를 카운트하여 정렬O(n+k) / O(n+k)안정, 키 범위 제한
기수 정렬(Radix Sort)자리수 단위로 정렬 후 병합O(nk) / O(nk)안정, 자리수 기반
버킷 정렬(Bucket Sort)데이터를 균등 분할 후 각 버킷 정렬O(n+k) / O(n²)평균적 효율 좋음

동적 프로그래밍

 큰 문제를 작은 부분 문제로 나누어 해결하고, 그 결과를 재사용하여 전체 문제를 효율적으로 해결하는 알고리즘 설계 기법이다. 특히, 재사용하는 방법에는 Top-Down 방식인 Memoization과 Bottom-Up 방식인 Tabulation이 있다.

네트워크

네트워크 OSI 7계층

  • 7층(Application Layer): 사용자와 직접 상호작용, 애플리케이션 데이터 처리
  • 6층(Presentation Layer): 데이터 형식 변환, 암호화/복호화, 인코딩/디코딩
  • 5층(Session Layer):통신 세션 관리, 연결 유지 및 종료
  • 4층(Transport Layer): 종단 간(end-to-end) 데이터 전송, 신뢰성 제공
  • 3층(Network Layer): 패킷 전송 및 경로 결정, IP 주소 기반 라우팅
  • 2층(Data Link Layer): 프레임 단위 전송, 오류 검출, MAC 주소
  • 1층(Physical Layer): 비트 단위 전송, 전송 매체 정의

HTTP 특징

HTTP 1.0과 1.1로 나누어 살펴보겠다. 공통적으론 다음 특징을 갖는다.

  1. 클라이언트 서버 구조: Request 주체인 Client와 그에 맞는 Response를 하는 Server로 구성
  2. 무상태성: server가 client의 상태를 보존하지 않는다.

1.0에서는 한 번의 요청과 한 번의 응답 이후 연결을 해제하는 비연결성 특징을 가졌다. 하지만 연속적인 통신에선 3-way handshake에 대한 오버로드가 커져 오히려 비효율적이였다. 이를 해결하기 위해 HTTP 1.1 버전은 다음과 같은 차이를 갖는다.

  1. 지속 연결: 단일 시간 내 연결 유지하여 TCP 연결을 최소화한다. 이를 통해 메모리 자원과 시간을 절약할 수 있다.
  2. 파이프라인: 응답을 기다리지 않고 단일 TCP 연결로 여러 요청을 보낼 수 있다.
  3. 호스트 헤더: 하나의 IP에 여러 도메인을 운영할 수 있도록 한다.

TCP와 UDP의 차이

TCP는 연결 지향으로 신뢰성 있는 데이터 전송를 목표로 한다. 따라서, 3-way Handshake로 연결 수립, 데이터 순서 보장 → Segment 번호 기반 재조립, 오류 검출 및 재전송, 흐름 제어와 혼잡 제어 제공 등의 기능을 제공한다.
UDP는 비연결(Connectionless)으로 신뢰성 없는 데이터 전송을 수행한다. 간단하고 빠르며 헤더 오버헤드 적다. 하지만 전송된 데이터의 순서를 보장하지 않고 손실 가능성도 있다. 따라서, 실시간 전송, 스트리밍, 게임 등에서 유리한 통신 프로토콜이다.

TCP 3-way handshake

 TCP/IP프로토콜을 이용해서 통신을 하는 응용프로그램이 데이터를 전송하기 전에 먼저 정확한 전송을 보장하기 위해 상대방 컴퓨터와 사전에 세션을 수립하는 과정을 의미한다. 다음과 같은 세 과정을 거친다.

  • Client > Server : SYN -> 접속 요청
  • Client < Server : SYN + ACK -> 요청 수락
  • Client > Server : ACK -> 연결 완료

TCP 4-way handshake

세션을 종료하기 위해 수행되는 절차이다.

  • Client > Server : FIN -> 해제 알림
  • Client < Server : ACK -> 확인(server가 TIME_WAIT 상태로 변경)
  • Client < Server : FIN -> 연결 종료
  • Client > Server : ACK -> 확인

RESTful API 란

 REST 아키텍처 원칙을 따르는 웹 API로, 자원과 HTTP 메서드를 경로로 표현하여 쉽게 사용할 수 있다. 또한, 무상태(Stateless) 구조, 일관된 표현 방식(JSON, XML)을 특징으로 하며, 서버는 클라이언트 요청에 대해 상태 코드(2xx, 4xx, 5xx)로 결과를 알린다.

profile
안녕하세요!

0개의 댓글