
안녕하세요 백엔드 관련 지식을 경험을 기반하여 정리해보려고 합니다. 위 마인드맵의 모든 양을 다루기에는 경험하지 못한 부분도 있고 비교적 방대하므로 마인드맵을 참고하여 아래 목차 순으로 진행하겠습니다.
(오른쪽에 있는 목차를 클릭하시면 해당 내용으로 이동가능합니다.)
인터넷 작동 원리
인터넷은 수많은 시스템이 연결된 거대한 분산 네트워크입니다. 사용자가 브라우저에 요청을 입력했을 때 내부적으로 일어나는 전체 흐름은 다음과 같습니다.
2번에서 브라우저는 IP 주소를 획득하기 위해 매번 네트워크 DNS 요청을 보내지 않습니다. 일반적으로 다음 순서로 확인합니다.
이 과정을 통해 도메인에 해당하는 IP 주소를 얻습니다.
IP 주소를 얻은 뒤, 브라우저는 먼저 소켓(Socket) 을 생성합니다.
소켓은 네트워크 통신을 위한 인터페이스이며, 이 단계에서는 아직 실제 네트워크 연결이 이루어지지 않습니다.
이후 브라우저가 connect(서버 IP, 포트) 를 호출하면, 그 시점에 커널이 TCP 연결을 생성하기 시작합니다. 이 과정에서 다음과 같은 TCP 3-way Handshake 가 수행됩니다.
이 handshake가 성공해야 TCP 연결이 성립됩니다. HTTPS의 경우 TCP 연결이 완료된 이후 TLS Handshake가 추가로 진행됩니다.
TCP 연결이 성립되면 브라우저는 HTTP 요청을 생성하고 send() 와 같은 Socket API를 호출합니다. 이때 데이터는 사용자 공간에서 커널 공간으로 전달되며, 커널은 이를 TCP 세그먼트로 분할한 뒤 NIC(Network Interface Card)를 통해 네트워크로 전송합니다.
서버에서는 listen() 상태의 소켓이 연결 요청을 대기하고 있으며, handshake가 완료되면 커널이 해당 연결을 생성합니다. 서버는 연결을 다음 4-tuple로 식별합니다.
(src IP, src port, dst IP, dst port)
이후 연결은 소켓 큐(accept queue)로 전달되고, 애플리케이션이 이를 accept() 하여 HTTP 요청을 처리합니다.
서버는 요청을 처리한 뒤 HTTP Response를 반환합니다. 응답 이후 연결을 종료할지, 유지할지는 HTTP 헤더의 Connection: keep-alive 여부에 따라 결정됩니다. HTTP/1.1에서는 기본적으로 연결을 재사용하며, 필요 시에만 종료합니다
HTTP(HyperText Transfer Protocol)는 웹에서 클라이언트와 서버 간 통신을 위한 애플리케이션 계층 프로토콜입니다.
HTTP의 특징
1. Stateless(무상태성)
HTTP 메시지 구조
HTTP 메시지는 요청(Request)과 응답(Response)으로 구분됩니다.
HTTP Request 구조:
Request Line: GET /api/users HTTP/1.1
Headers: Host, User-Agent, Accept, Content-Type 등
Body: (POST, PUT 등에서 사용)
HTTP Response 구조:
Status Line: HTTP/1.1 200 OK
Headers: Content-Type, Content-Length, Set-Cookie 등
Body: 실제 데이터
HTTP 메서드
HTTP 상태 코드
1xx: 정보성 응답
2xx: 성공
3xx: 리다이렉션
4xx: 클라이언트 오류
5xx: 서버 오류
HTTP 버전별 특징
HTTP/1.0
HTTP/1.1
HTTP/2
HTTP/3
DNS(Domain Name System)는 도메인 이름을 IP 주소로 변환하는 분산 계층형 네이밍 시스템입니다.
DNS 조회 과정 (Recursive Query)
DNS 캐싱과 TTL
프로세스란?
프로세스는 실행 중인 프로그램의 인스턴스이며,
운영체제로부터 독립적인 가상 메모리 공간을 할당받는 실행 단위입니다.
즉, 단순한 프로그램 파일(.exe, .jar 등)이 아니라
메모리를 할당받아 실제로 동작 중인 상태가 프로세스입니다.
프로세스의 메모리 구조
프로세스는 OS로부터 가상 메모리(Virtual Memory) 공간을 할당받습니다.
이 가상 메모리는 실제 물리 메모리(RAM)와 디스크(스왑 영역)를 매핑하여 관리됩니다.
가상 메모리란?
각 프로세스는 자기만의 독립적인 주소 공간을 가짐
실제 물리 메모리는 여러 프로세스가 공유
MMU(Memory Management Unit)가 가상 주소 → 물리 주소 변환
메모리가 부족하면 일부 페이지를 디스크(Swap)로 내림 (1차/2차 메모리 혼합 사용) 프로세스는 물리 메모리를 직접 쓰는 것이 아니라 OS가 관리하는 가상 메모리 공간을 통해 간접적으로 사용합니다.
프로세스 메모리 구성
코드 영역(Text)
데이터 영역(Data)
런타임에 크기 변화
프로세스의 특징
독립적인 가상 메모리 공간
다른 프로세스와 기본적으로 메모리 격리
프로세스 간 통신(IPC) 필요
(파이프, 소켓, 공유 메모리, 메시지 큐 등)
컨텍스트 스위칭 비용이 상대적으로 큼
프로세스 상태
스레드란?
스레드는 프로세스 내에서 실행되는 작업의 최소 단위입니다. 하나의 프로세스는 여러 스레드를 가질 수 있으며,
같은 프로세스의 스레드들은 메모리를 공유합니다.
스레드의 메모리 구조
스레드는 다음을 공유합니다:
코드 영역
데이터 영역
힙 영역
하지만 다음은 스레드별로 독립적으로 가집니다:
스택(Stack)
프로그램 카운터(PC)
레지스터 집합
스레드는 메모리 대부분을 공유하지만
실행 흐름에 필요한 최소 상태만 따로 가집니다.
스레드의 특징
프로세스 내 메모리 공유
스레드별 독립 스택
컨텍스트 스위칭 비용이 낮음
동기화 필요 (Race Condition, Deadlock 등)
프로세스 vs 스레드 — 컨텍스트 스위칭 차이
컨텍스트 스위칭이란?
CPU가 한 실행 단위에서 다른 실행 단위로 전환될 때
기존 상태를 저장하고 새로운 상태를 복원하는 과정입니다.
저장/복원되는 정보:
왜 프로세스가 더 비싼가?
주소 공간 전환
프로세스는 서로 다른 가상 메모리 공간을 사용합니다.
프로세스 전환 시:
페이지 테이블 교체
TLB(Translation Lookaside Buffer) flush
메모리 매핑 정보 변경
이 작업이 매우 비용이 큽니다.
캐시 오염(Cache Pollution)
다른 프로세스로 전환되면:
CPU 캐시 내용이 무효화되거나 효율이 떨어짐
새로운 메모리 데이터를 다시 로딩해야 함
왜 스레드는 상대적으로 빠른가?
스레드는 같은 프로세스의 주소 공간을 공유합니다.
따라서:
페이지 테이블 교체 불필요
TLB flush 거의 없음
메모리 매핑 유지
스레드 전환은 레지스터/스택 상태만 바꾸면 되므로 비용이 낮습니다
동시성(Concurrency) vs 병렬성(Parallelism)
동시성:
병렬성:
동시성 제어의 필요성
여러 스레드가 공유 자원에 접근할 때 동기화가 필요합니다.
경쟁 조건(Race Condition):
// 스레드 A와 B가 동시에 count++ 실행
int count = 0;
// 스레드 A: count = 0 → 읽음 → 1 → 쓰기
// 스레드 B: count = 0 → 읽음 → 1 → 쓰기
// 결과: count = 1 (기대값: 2)
원자성(Atomicity)이 보장되지 않아 문제가 발생합니다.
동기화 메커니즘
1. 뮤텍스(Mutex)
데드락(Deadlock)
두 개 이상의 스레드가 서로의 자원을 기다리며 무한 대기하는 상태입니다.
데드락 발생 조건(필요 조건):
데드락 해결 방법:
Thread Pool
I/O의 기본 개념
I/O는 CPU와 메모리 외부 장치(디스크, 네트워크 등) 간 데이터 전송입니다.
I/O의 특징:
I/O 모델
1. 동기 블로킹 I/O
TCP/IP 4계층 모델
실제 인터넷에서 사용하는 모델입니다.
TCP vs UDP
TCP (Transmission Control Protocol):
UDP (User Datagram Protocol):
TCP의 핵심 메커니즘
Client → Server: SYN (seq=x)Server → Client: SYN-ACK (seq=y, ack=x+1)Client → Server: ACK (seq=x+1, ack=y+1)
Client → Server: FINServer → Client: ACKServer → Client: FINClient → Server: ACK (TIME_WAIT 상태)
포트(Port)
프로세스를 식별하는 논리적 주소입니다.
포트 범위:
소켓(Socket)
네트워크 통신의 엔드포인트입니다.
소켓 생성 과정:
1. socket(): 소켓 생성
2. bind(): 주소 바인딩(서버)
3. listen(): 연결 대기(서버)
4. accept(): 연결 수락(서버)
5. connect(): 연결 요청(클라이언트)
6. send()/recv(): 데이터 송수신
7. close(): 연결 종료
JVM 구조
JVM의 역할
JVM(Java Virtual Machine)은 Java 바이트코드를 실행하는 가상 머신입니다.
JVM의 주요 기능:
1. 바이트코드 로딩 및 실행
2. 메모리 관리(힙, 스택 등)
3. 가비지 컬렉션
4. 스레드 관리
JVM 구조
메모리 관리
힙(Heap) 메모리 구조
힙은 객체 인스턴스가 저장되는 영역입니다.
힙 영역 구분:
1. Young Generation
가비지 컬렉션(GC)
GC의 기본 원리:
GC 알고리즘:
1. Serial GC: 단일 스레드, 작은 애플리케이션
2. Parallel GC: 멀티스레드, 처리량 중심
3. CMS GC: 낮은 지연 시간 목표(Java 9+ deprecated)
4. G1 GC: 대용량 힙, 일정한 지연 시간 목표
5. ZGC: 매우 낮은 지연 시간(Java 11+)
6. Shenandoah: 낮은 지연 시간(Java 12+)
GC 최적화 전략:
스택(Stack) 메모리
스택은 메서드 호출 정보를 저장합니다.
스택 프레임 구성:
스택 오버플로우:
-Xss 옵션으로 스택 크기 조정 가능객체 지향 프로그래밍
OOP의 4대 원칙
접근 제어자
컬렉션 프레임워크
HashMap 동작 원리
HashMap은 해시 테이블 기반의 키-값 저장 구조입니다.
HashMap의 내부 구조:
// Java 8+ 기준
transient Node<K,V>[] table; // 버킷 배열
static class Node<K,V> {
final int hash;
final K key;
V value;
Node<K,V> next; // LinkedList 연결
}
해시 함수와 인덱스 계산:
// 해시값 계산
int hash = key.hashCode();
// 인덱스 계산 (비트 연산 사용)
int index = (table.length - 1) & hash;
2의 배수로 리사이즈하는 이유:
해시 충돌 처리:
1. Separate Chaining
리사이즈 과정:
// 기본 초기 용량: 16
// 로드 팩터: 0.75 (75% 채워지면 리사이즈)
// 리사이즈: 기존 크기의 2배 (16 → 32 → 64 → ...)
// 리사이즈 시 재해싱
// 기존: index = hash & (16 - 1) = hash & 1111
// 신규: index = hash & (32 - 1) = hash & 11111
// 추가 비트로 인해 인덱스가 변경될 수 있음
HashMap의 성능:
HashMap 사용 시 주의사항:
1. 키 객체는 equals()와 hashCode()를 올바르게 구현
2. 불변 객체를 키로 사용 권장
3. 초기 용량과 로드 팩터 고려
HashSet 동작 원리
HashSet은 HashMap을 기반으로 구현됩니다.
HashSet의 내부 구현:
private transient HashMap<E,Object> map;
private static final Object PRESENT = new Object();
public boolean add(E e) {
return map.put(e, PRESENT) == null;
}
HashSet의 특징:
다른 주요 컬렉션
ArrayList:
LinkedList:
TreeMap:
ConcurrentHashMap:
동시성 프로그래밍
synchronized
synchronized의 동작:
synchronized 사용:
// 메서드 동기화
public synchronized void method() { }
// 블록 동기화
synchronized (object) { }
// 정적 메서드 동기화 (클래스 락)
public static synchronized void method() { }
synchronized의 한계:
예외처리
예외 계층 구조
Throwable
├── Error (시스템 레벨 오류)
│ ├── OutOfMemoryError
│ └── StackOverflowError
└── Exception
├── RuntimeException (Unchecked Exception)
│ ├── NullPointerException
│ ├── IllegalArgumentException
│ └── IndexOutOfBoundsException
└── Checked Exception
├── IOException
└── SQLException
Checked vs Unchecked Exception
Checked Exception:
Unchecked Exception:
제네릭(Generics)
제네릭의 필요성
타입 안전성:
코드 재사용:
제네릭 사용법
// 클래스 제네릭
class Box<T> {
private T item;
public void setItem(T item) { this.item = item; }
public T getItem() { return item; }
}
// 메서드 제네릭
public <T> T getFirst(List<T> list) {
return list.get(0);
}
// 와일드카드
List<? extends Number> // 상한 제한
List<? super Integer> // 하한 제한
타입 소거(Type Erasure)
람다와 스트림
람다 표현식
함수형 인터페이스:
스트림 API
스트림의 특징:
스트림 연산:
// 중간 연산 (Lazy)filter(), map(), sorted(), distinct()
// 최종 연산 (Eager)forEach(), collect(), reduce(), count()
스트림 사용 예:
list.stream()
.filter(x -> x > 10)
.map(x -> x * 2)
.sorted()
.collect(Collectors.toList());
JIT 컴파일
인터프리터 vs JIT:
최적화 기법:
메모리 튜닝
힙 크기 설정:
-Xms: 초기 힙 크기-Xmx: 최대 힙 크기-XX:NewRatio: Young/Old 비율GC 튜닝:
트랜잭션이란?
트랜잭션은 데이터베이스의 논리적 작업 단위로, 모두 성공하거나 모두 실패해야 합니다(All or Nothing).
트랜잭션의 필요성:
트랜잭션 예시:
-- 계좌 이체 예시
BEGIN TRANSACTION;
UPDATE Account SET balance = balance - 1000 WHERE id = 1;
UPDATE Account SET balance = balance + 1000 WHERE id = 2;
COMMIT;
-- 둘 다 성공하거나 둘 다 실패해야 함
트랜잭션 특징 (ACID)
트랜잭션 격리 수준
격리 수준별 발생 가능한 문제:
| 격리 수준 | Dirty Read | Non-Repeatable Read | Phantom Read |
|---|---|---|---|
| READ UNCOMMITTED | 가능 | 가능 | 가능 |
| READ COMMITTED | 불가능 | 가능 | 가능 |
| REPEATABLE READ | 불가능 | 불가능 | 가능 |
| SERIALIZABLE | 불가능 | 불가능 | 불가능 |
Undo Log와 Redo Log
Redo Log (재실행 로그):
Redo Log 동작:
1. 트랜잭션이 데이터 변경
2. 변경 내용을 Redo Log에 기록 (WAL: Write-Ahead Logging)
3. 메모리 버퍼에 변경 사항 반영
4. 커밋 시 Redo Log를 디스크에 플러시
5. 장애 발생 시 Redo Log를 재실행하여 복구
Undo Log (취소 로그):
Undo Log 동작:
1. 트랜잭션이 데이터 변경 전 상태를 Undo Log에 기록
2. 데이터 변경 수행
3. 롤백 시 Undo Log를 역순으로 적용하여 원상복구
4. MVCC에서 과거 버전 재구성에 사용
차이점 요약:
| 구분 | Redo Log | Undo Log |
|---|---|---|
| 목적 | 복구 (Recovery) | 롤백 (Rollback) |
| 내용 | 변경 후 상태 | 변경 전 상태 |
| 방향 | 순방향 재실행 | 역방향 복원 |
| 사용 | 커밋 후 장애 복구 | 롤백, MVCC |
Non-Blocking Consistent Read
개념:
일관된 읽기 (Consistent Read):
MVCC 동작 원리:
1. 각 행에 버전 정보 저장 (트랜잭션 ID, 타임스탬프 등)
2. 트랜잭션 시작 시 Read View 생성
3. 읽기 시 자신보다 이전에 커밋된 버전만 읽음
4. 쓰기 시 새로운 버전 생성
5. Undo Log를 통해 과거 버전 재구성
예시:
-- 트랜잭션 A (시작 시간: T1)
BEGIN;
SELECT * FROM users WHERE id = 1; -- 결과: name='John'
-- 트랜잭션 B (시작 시간: T2, T1 < T2)
BEGIN;
UPDATE users SET name='Jane' WHERE id = 1;
COMMIT;
-- 트랜잭션 A에서 다시 조회
SELECT * FROM users WHERE id = 1; -- 결과: name='John' (스냅샷 읽기)
COMMIT;
장점:
단점:
데이터 버전과 스냅샷
데이터 버전:
데이터 스냅샷:
Read View 구성 요소:
버전 가시성 판단:
행의 버전이 보이는지 판단:
1. creator_trx_id와 같으면 → 보임 (자신이 생성)
2. min_trx_id보다 작고 m_ids에 없으면 → 보임 (이미 커밋)
3. max_trx_id보다 크면 → 안 보임 (미래)
4. m_ids에 있으면 → 안 보임 (아직 커밋 안 됨)
락의 필요성
경쟁 조건 (Race Condition):
락의 목적:
락의 종류
락 호환성 매트릭스:
| S-Lock | X-Lock | |
|---|---|---|
| S-Lock | 호환 | 비호환 |
| X-Lock | 비호환 | 비호환 |
락의 범위 (Lock Granularity)
데드락 (Deadlock)
데드락 발생 조건:
1. 상호 배제: 자원이 배타적
2. 점유 대기: 자원을 보유한 채 대기
3. 비선점: 자원을 강제로 빼앗을 수 없음
4. 순환 대기: 순환 형태의 대기 체인
데드락 예시:
트랜잭션 A: Row 1 락 획득 → Row 2 락 대기
트랜잭션 B: Row 2 락 획득 → Row 1 락 대기
→ 순환 대기 발생 (데드락)
데드락 처리 기법
락 타임아웃
타임아웃 설정:
타임아웃 처리:
-- MySQL 예시
SET innodb_lock_wait_timeout = 50; -- 50초 대기 후 타임아웃
낙관적 락 vs 비관적 락
비관적 락 (Pessimistic Lock):
낙관적 락 (Optimistic Lock):
낙관적 락 구현:
-- 버전 컬럼 사용
UPDATE users
SET name = 'John', version = version + 1
WHERE id = 1 AND version = 5;
-- 영향받은 행이 0이면 버전 충돌 (다른 트랜잭션이 먼저 수정)
인덱스와 락
인덱스 락:
갭 락 (Gap Lock):
Next-Key Lock:
락 최적화 전략
CAP 이론의 개념
CAP 이론은 분산 시스템에서 다음 세 가지를 동시에 만족할 수 없음을 설명합니다.
C (Consistency) - 일관성:
A (Availability) - 가용성:
P (Partition Tolerance) - 분할 허용성:
CAP 정리의 의미
핵심 원리:
CP 시스템 (Consistency + Partition Tolerance):
AP 시스템 (Availability + Partition Tolerance):
CA 시스템 (Consistency + Availability):
일관성의 종류
강한 일관성 (Strong Consistency):
최종 일관성 (Eventual Consistency):
약한 일관성 (Weak Consistency):
CAP 이론의 한계
실제 시스템의 복잡성:
CAP 이론의 실제 적용
관계형 데이터베이스:
NoSQL 데이터베이스:
하이브리드 접근:
CAP 이론과 데이터베이스 선택
CP가 적합한 경우:
AP가 적합한 경우:
실무 고려사항:
캐싱이란?
캐싱은 자주 사용하는 데이터를 빠른 저장소에 보관해 응답 속도를 높이는 기법입니다.
캐싱의 목적:
캐싱의 원리:
캐시 계층 구조
캐시 전략
1. 애플리케이션이 캐시 확인
2. 캐시 미스 시 DB 조회
3. DB 결과를 캐시에 저장
4. 결과 반환
1. 데이터를 캐시와 DB에 동시에 쓰기2. 항상 캐시와 DB 동기화
1. 데이터를 캐시에만 먼저 쓰기
2. 비동기로 DB에 쓰기
1. 캐시 만료 전에 미리 갱신
2. 사용자 요청 전에 백그라운드 갱신
Redis(Remote Dictionary Server)는 인메모리 키-값 저장소입니다.
Redis의 특징:
Redis의 주요 용도:
Redis 데이터 구조
Redis 영속성
RDB 설정:
save 900 1 # 900초 동안 1개 이상 변경 시 저장
save 300 10 # 300초 동안 10개 이상 변경 시 저장
save 60 10000 # 60초 동안 10000개 이상 변경 시 저장
AOF 설정:
appendonly yes
appendfsync everysec # 매초마다 디스크에 동기화
Redis 메모리 관리
메모리 제한:
maxmemory 2gb
maxmemory-policy allkeys-lru # LRU 정책으로 키 제거
제거 정책 (Eviction Policy):
LRU vs LFU:
Redis 클러스터링
1. Redis Sentinel
Sentinel 동작:
1. Sentinel이 마스터와 슬레이브 모니터링
2. 마스터 장애 감지
3. 쿼럼(과반수) 달성 시 장애 조치
4. 슬레이브를 마스터로 승격
5. 클라이언트에 새 마스터 정보 전달
Cluster 샤딩:
Cluster 라우팅:
1. 클라이언트가 키의 해시 슬롯 계산
2. 해당 슬롯을 가진 노드로 요청
3. 노드가 다른 노드에 있으면 MOVED 에러와 함께 올바른 노드 정보 반환
4. 클라이언트가 새 노드로 재요청
Redis 분산 락
기본 구현:
-- SET key value NX EX timeoutS
ET lock_key unique_value NX EX 30
문제점:
개선된 구현 (Redlock 알고리즘):
1. 현재 시간 기록
2. N개 Redis 인스턴스에 순차적으로 락 획득 시도
3. 과반수 이상 획득 성공 시 락 획득
4. 락 유효 시간 = TTL - (현재 시간 - 시작 시간) - 클럭 드리프트
5. 락 해제 시 모든 인스턴스에서 해제
주의사항:
Redis 트랜잭션
MULTI/EXEC:
MULTISET key1 value1SET key2 value2EXEC
WATCH:
CDN(Content Delivery Network)은 지리적으로 분산된 서버 네트워크로, 사용자와 가까운 위치에서 콘텐츠를 제공합니다.
CDN의 목적:
CDN의 동작 원리:
1. 사용자가 웹사이트 요청
2. DNS가 사용자와 가장 가까운 CDN 엣지 서버 반환
3. 엣지 서버가 캐시 확인
4. 캐시 히트 시 즉시 제공
5. 캐시 미스 시 원본 서버에서 가져와 캐시 후 제공
CDN 아키텍처
CDN 캐싱 전략
CDN 캐시 무효화
CDN 사용 사례
CDN vs Redis 비교
| 구분 | CDN | Redis |
|---|---|---|
| 용도 | 정적 콘텐츠 배포 | 동적 데이터 캐싱 |
| 위치 | 전 세계 분산 | 애플리케이션 서버 근처 |
| 데이터 타입 | 파일, 미디어 | 구조화된 데이터 |
| TTL | 보통 길음 (시간~일) | 보통 짧음 (초~분) |
| 무효화 | 수동 또는 TTL | 프로그래밍 방식 |
| 비용 | 트래픽 기반 | 메모리 기반 |
캐싱 전략 통합
다층 캐싱:
사용자 → CDN → 애플리케이션 캐시(Redis) → 데이터베이스
각 계층의 역할:
캐시 일관성: