Skip to content
Published on

네트워크 엔지니어링 & 보안 완전 정복: TCP/IP부터 서비스 메시, AI 서빙 네트워크까지

Authors

목차

  1. 네트워크 기초: OSI & TCP/IP
  2. 소켓 프로그래밍: asyncio & aiohttp
  3. 서비스 메시: Istio & Envoy
  4. 네트워크 보안: TLS & Zero Trust
  5. CDN & 엣지 컴퓨팅
  6. AI 서빙 네트워크: gRPC & SSE
  7. 패킷 분석 실전
  8. 퀴즈

1. 네트워크 기초

OSI 7계층 모델

OSI(Open Systems Interconnection) 모델은 네트워크 통신을 7개의 계층으로 추상화한 참조 모델입니다.

계층이름프로토콜 예시PDU
7ApplicationHTTP, DNS, SMTPMessage
6PresentationTLS, JPEG, ASCIIMessage
5SessionNetBIOS, RPCMessage
4TransportTCP, UDPSegment
3NetworkIP, ICMPPacket
2Data LinkEthernet, 802.11Frame
1Physical케이블, 광섬유Bits

TCP/IP 스택

실제 인터넷은 OSI보다 단순화된 4계층 TCP/IP 모델을 사용합니다.

  • Application Layer: HTTP/2, HTTP/3, DNS, TLS
  • Transport Layer: TCP (신뢰성), UDP (속도)
  • Internet Layer: IPv4, IPv6, ICMP
  • Link Layer: Ethernet, Wi-Fi

HTTP/2와 HTTP/3의 차이

HTTP/1.1은 하나의 연결에서 한 번에 하나의 요청만 처리할 수 있어 Head-of-Line(HOL) 블로킹이 발생합니다.

HTTP/2 개선점:

  • 멀티플렉싱: 하나의 TCP 연결에서 여러 스트림 동시 처리
  • 헤더 압축: HPACK 알고리즘으로 중복 헤더 제거
  • 서버 푸시: 클라이언트 요청 없이 리소스 선제 전송
  • 바이너리 프레이밍: 텍스트 대신 바이너리 프레임 사용

HTTP/3 & QUIC: HTTP/3는 TCP 대신 QUIC(UDP 기반) 위에서 동작하여 TCP 레벨 HOL 블로킹까지 해결합니다.

HTTP/1.1:  [Req1][Res1][Req2][Res2]  (순차)
HTTP/2:    [Req1, Req2, Req3][Res1, Res2, Res3]  (멀티플렉싱, 단일 TCP)
HTTP/3:    [Req1, Req2, Req3][Res1, Res2, Res3]  (멀티플렉싱, 독립 QUIC 스트림)

DNS 동작 원리

api.example.com을 조회할 때의 흐름:

  1. 브라우저 캐시 확인
  2. OS 캐시 확인 (/etc/hosts)
  3. Recursive Resolver(ISP DNS)에 질의
  4. Root NS → .com TLD NS → example.com Authoritative NS 순서로 계층적 질의
  5. A 레코드(IPv4) 또는 AAAA 레코드(IPv6) 반환

TLS 1.3 핸드셰이크

TLS 1.3은 이전 버전보다 왕복 횟수를 줄여 1-RTT(최초 연결) 또는 0-RTT(재연결)로 세션을 수립합니다.

Client                          Server
  |--- ClientHello (키 교환) ---->|
  |<-- ServerHello + Certificate-|
  |<-- + EncryptedExtensions -----|
  |--- Finished ----------------->|
  |<-> 암호화된 데이터 교환 <------>|

2. 소켓 프로그래밍

Python asyncio TCP 서버

import asyncio

async def handle_client(reader: asyncio.StreamReader, writer: asyncio.StreamWriter):
    addr = writer.get_extra_info('peername')
    print(f"연결됨: {addr}")

    try:
        while True:
            data = await reader.read(1024)
            if not data:
                break
            message = data.decode('utf-8').strip()
            print(f"수신: {message} from {addr}")

            # 에코 응답
            response = f"Echo: {message}\n"
            writer.write(response.encode('utf-8'))
            await writer.drain()
    except asyncio.IncompleteReadError:
        pass
    finally:
        print(f"연결 종료: {addr}")
        writer.close()
        await writer.wait_closed()

async def main():
    server = await asyncio.start_server(
        handle_client, '0.0.0.0', 8888
    )
    addr = server.sockets[0].getsockname()
    print(f"서버 시작: {addr}")

    async with server:
        await server.serve_forever()

if __name__ == '__main__':
    asyncio.run(main())

aiohttp를 사용한 비동기 HTTP 클라이언트

import asyncio
import aiohttp
from typing import List

async def fetch_url(session: aiohttp.ClientSession, url: str) -> dict:
    async with session.get(url, timeout=aiohttp.ClientTimeout(total=10)) as response:
        return {
            'url': url,
            'status': response.status,
            'body': await response.text()
        }

async def fetch_all(urls: List[str]) -> List[dict]:
    connector = aiohttp.TCPConnector(
        limit=100,          # 최대 동시 연결 수
        limit_per_host=10,  # 호스트당 최대 연결
        keepalive_timeout=30
    )
    async with aiohttp.ClientSession(connector=connector) as session:
        tasks = [fetch_url(session, url) for url in urls]
        return await asyncio.gather(*tasks, return_exceptions=True)

# 실행
urls = [f"https://httpbin.org/get?id={i}" for i in range(20)]
results = asyncio.run(fetch_all(urls))

gRPC vs REST 비교

항목gRPCREST
프로토콜HTTP/2HTTP/1.1 or HTTP/2
직렬화Protocol Buffers (바이너리)JSON (텍스트)
스트리밍양방향 스트리밍 지원제한적 (SSE, WebSocket 별도)
타입 안전성강한 타입 (.proto 스키마)약한 타입
레이턴시낮음상대적으로 높음
브라우저 지원grpc-web 필요네이티브

3. 서비스 메시

Istio 아키텍처

Istio는 사이드카 패턴으로 Envoy 프록시를 각 Pod에 주입하여 서비스 간 통신을 제어합니다.

  • Control Plane (Istiod): 설정 관리, 인증서 발급, 트래픽 정책 배포
  • Data Plane (Envoy Sidecar): 실제 트래픽 처리, 메트릭 수집, mTLS 적용

VirtualService 설정 예시

apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
  name: ml-inference-vs
  namespace: production
spec:
  hosts:
    - ml-inference-svc
  http:
    - match:
        - headers:
            x-model-version:
              exact: 'v2'
      route:
        - destination:
            host: ml-inference-svc
            subset: v2
          weight: 100
    - route:
        - destination:
            host: ml-inference-svc
            subset: v1
          weight: 90
        - destination:
            host: ml-inference-svc
            subset: v2
          weight: 10

로드 밸런싱 알고리즘

  • Round Robin: 순서대로 분산 (기본값)
  • Least Connections: 활성 연결이 가장 적은 서버 선택
  • Weighted Round Robin: 가중치 기반 분산
  • IP Hash: 클라이언트 IP로 특정 서버에 고정 (세션 유지)
  • Consistent Hashing: 분산 캐시 환경에서 서버 추가/제거 시 재분산 최소화

서비스 디스커버리

Kubernetes 환경에서는 CoreDNS가 서비스 디스커버리를 담당합니다. service-name.namespace.svc.cluster.local 형식으로 서비스에 접근합니다.


4. 네트워크 보안

TLS 인증서 체인

Root CA (최상위 인증기관, 브라우저/OS에 내장)
  └── Intermediate CA (중간 인증기관)
        └── Leaf Certificate (실제 서버 인증서)

각 인증서는 상위 CA의 개인키로 서명되어 신뢰 체인을 형성합니다.

mTLS (Mutual TLS)

일반 TLS는 서버만 인증서를 제공합니다. mTLS는 클라이언트도 인증서를 제공하여 양방향 인증을 수행합니다.

일반 TLS:  Client[서버 인증서 검증]Server
mTLS:      Client[양방향 인증서 검증]Server

서비스 메시에서 mTLS는 사이드카 프록시 간의 통신을 자동으로 암호화하고 인증합니다.

Nginx TLS 설정

server {
    listen 443 ssl http2;
    server_name api.example.com;

    ssl_certificate     /etc/ssl/certs/server.crt;
    ssl_certificate_key /etc/ssl/private/server.key;

    # TLS 1.2 이상만 허용
    ssl_protocols TLSv1.2 TLSv1.3;
    ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256;
    ssl_prefer_server_ciphers off;

    # HSTS (1년)
    add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;

    # OCSP Stapling
    ssl_stapling on;
    ssl_stapling_verify on;

    location /api/ {
        proxy_pass http://backend_pool;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
    }
}

Zero Trust 아키텍처

Zero Trust의 핵심 원칙: "절대 신뢰하지 말고, 항상 검증하라 (Never Trust, Always Verify)"

  • ID 기반 접근 제어: IP 주소 대신 사용자/서비스 ID로 인증
  • 최소 권한 원칙: 필요한 리소스에만 접근 허용
  • 지속적 검증: 세션 중에도 지속적으로 신뢰 수준 평가
  • 마이크로 세그멘테이션: 네트워크를 작은 구역으로 분리하여 측면 이동 차단

JWT & OAuth 2.0

JWT(JSON Web Token)는 세 부분으로 구성됩니다: Header.Payload.Signature

OAuth 2.0 Authorization Code Flow:

  1. 클라이언트가 Authorization Server로 리다이렉트
  2. 사용자 인증 후 Authorization Code 발급
  3. 백엔드에서 Code를 Access Token으로 교환
  4. Access Token으로 Resource Server API 호출

5. CDN & 엣지 컴퓨팅

CDN 작동 원리

CDN(Content Delivery Network)은 전 세계 PoP(Point of Presence)에 콘텐츠를 캐싱하여 지연 시간을 줄입니다.

캐시 전략:

  • Cache-Control: max-age=86400: 브라우저 및 CDN에서 1일 캐시
  • Cache-Control: no-cache: 항상 원본 서버에 재검증 요청
  • ETag / Last-Modified: 조건부 요청으로 변경 여부 확인

Cloudflare Workers 예시

export default {
  async fetch(request, env) {
    const url = new URL(request.url)

    // 엣지에서 AI 추론 라우팅
    if (url.pathname.startsWith('/inference/')) {
      const modelId = url.searchParams.get('model') || 'default'

      // 가장 가까운 GPU 클러스터로 라우팅
      const region = request.cf.region
      const backendUrl = selectBackend(region, modelId)

      return fetch(backendUrl, {
        method: request.method,
        headers: request.headers,
        body: request.body,
      })
    }

    // 정적 자산 캐싱
    const cache = caches.default
    const cachedResponse = await cache.match(request)
    if (cachedResponse) return cachedResponse

    const response = await fetch(request)
    // 성공 응답만 캐싱
    if (response.status === 200) {
      const responseToCache = response.clone()
      await cache.put(request, responseToCache)
    }
    return response
  },
}

6. AI 서빙 네트워크

gRPC를 활용한 ML 모델 서빙

Protocol Buffers 정의:

syntax = "proto3";
package inference;

service InferenceService {
  rpc Predict(PredictRequest) returns (PredictResponse);
  rpc StreamPredict(PredictRequest) returns (stream PredictResponse);
}

message PredictRequest {
  string model_name = 1;
  repeated float input_data = 2;
  map<string, string> metadata = 3;
}

message PredictResponse {
  repeated float output_data = 1;
  float confidence = 2;
  int64 latency_ms = 3;
}

Python gRPC 서버:

import grpc
from concurrent import futures
import inference_pb2
import inference_pb2_grpc
import numpy as np
import time

class InferenceServicer(inference_pb2_grpc.InferenceServiceServicer):
    def Predict(self, request, context):
        start = time.time()
        input_array = np.array(request.input_data)

        # 실제 모델 추론 (예시)
        output = input_array * 2.0

        latency = int((time.time() - start) * 1000)
        return inference_pb2.PredictResponse(
            output_data=output.tolist(),
            confidence=0.95,
            latency_ms=latency
        )

    def StreamPredict(self, request, context):
        # 스트리밍 응답 (LLM 토큰 생성 등에 활용)
        tokens = ["안녕", "하세요", "!", " gRPC", " 스트리밍", "입니다."]
        for token in tokens:
            yield inference_pb2.PredictResponse(
                output_data=[float(ord(c)) for c in token],
                confidence=0.9,
                latency_ms=10
            )

def serve():
    server = grpc.server(futures.ThreadPoolExecutor(max_workers=10))
    inference_pb2_grpc.add_InferenceServiceServicer_to_server(
        InferenceServicer(), server
    )
    server.add_insecure_port('[::]:50051')
    server.start()
    print("gRPC 서버 시작: 포트 50051")
    server.wait_for_termination()

SSE를 활용한 LLM 실시간 스트리밍

from fastapi import FastAPI
from fastapi.responses import StreamingResponse
import asyncio
import json

app = FastAPI()

async def llm_token_generator(prompt: str):
    """LLM 토큰을 SSE 형식으로 스트리밍"""
    # 실제 환경에서는 LLM 라이브러리 사용
    tokens = prompt.split() + ["[완료]"]
    for i, token in enumerate(tokens):
        data = json.dumps({"token": token, "index": i})
        yield f"data: {data}\n\n"
        await asyncio.sleep(0.05)  # 토큰 생성 시뮬레이션
    yield "data: [DONE]\n\n"

@app.get("/stream")
async def stream_llm(prompt: str = "안녕하세요"):
    return StreamingResponse(
        llm_token_generator(prompt),
        media_type="text/event-stream",
        headers={
            "Cache-Control": "no-cache",
            "X-Accel-Buffering": "no",  # Nginx 버퍼링 비활성화
        }
    )

7. 패킷 분석 실전

tcpdump 기본 명령어

# 특정 인터페이스에서 HTTP 트래픽 캡처
sudo tcpdump -i eth0 -w capture.pcap port 80 or port 443

# TCP 핸드셰이크만 필터링 (SYN 패킷)
sudo tcpdump -i any 'tcp[tcpflags] & tcp-syn != 0'

# 특정 호스트와의 통신
sudo tcpdump -i eth0 host 10.0.0.1 -n

# DNS 쿼리 모니터링
sudo tcpdump -i any udp port 53 -v

# gRPC (HTTP/2) 트래픽
sudo tcpdump -i eth0 port 50051 -w grpc_trace.pcap

curl을 이용한 네트워크 디버깅

# TLS 인증서 정보 확인
curl -vI https://api.example.com 2>&1 | grep -A 20 "SSL connection"

# HTTP/2 사용 여부 확인
curl --http2 -I https://api.example.com

# 응답 시간 측정
curl -o /dev/null -s -w \
  "DNS: %{time_namelookup}s\nTCP: %{time_connect}s\nTLS: %{time_appconnect}s\nTotal: %{time_total}s\n" \
  https://api.example.com

# gRPC 엔드포인트 테스트 (grpcurl)
grpcurl -plaintext localhost:50051 list
grpcurl -plaintext -d '{"model_name": "bert", "input_data": [1.0, 2.0]}' \
  localhost:50051 inference.InferenceService/Predict

네트워크 성능 지표

  • RTT (Round-Trip Time): 패킷 왕복 시간, ping으로 측정
  • Throughput: 단위 시간당 전송 데이터량, iperf3으로 측정
  • Packet Loss: 손실률, 불안정한 UDP 환경에서 중요
  • Jitter: RTT 변동성, 실시간 스트리밍에서 중요
# 네트워크 대역폭 측정
iperf3 -s  # 서버
iperf3 -c server_ip -t 30 -P 4  # 클라이언트 (4개 병렬 스트림)

퀴즈

Q1. TCP 3-way handshake와 4-way termination 과정을 설명하세요.

정답:

3-way handshake (연결 수립):

  1. Client → Server: SYN (seq=x)
  2. Server → Client: SYN-ACK (seq=y, ack=x+1)
  3. Client → Server: ACK (ack=y+1)

4-way termination (연결 종료):

  1. Client → Server: FIN
  2. Server → Client: ACK
  3. Server → Client: FIN
  4. Client → Server: ACK (TIME_WAIT 상태 후 완전 종료)

설명: 연결 종료가 4단계인 이유는 서버가 FIN을 받은 후에도 남은 데이터를 전송할 수 있기 때문입니다. 클라이언트의 ACK 이후 TIME_WAIT 상태는 2MSL(Maximum Segment Lifetime) 동안 유지되어 지연 패킷을 처리합니다.

Q2. HTTP/2의 multiplexing이 HTTP/1.1의 HOL blocking을 해결하는 방식을 설명하세요.

정답: HTTP/2는 하나의 TCP 연결에서 여러 스트림을 동시에 전송합니다. 각 스트림은 독립적인 ID를 가지며, 한 스트림의 처리가 지연되어도 다른 스트림은 영향받지 않습니다.

설명: HTTP/1.1은 파이프라이닝을 지원하지만 응답은 요청 순서대로 받아야 해서 앞의 응답이 느리면 뒤의 응답도 대기합니다 (HOL blocking). HTTP/2는 프레임 단위로 인터리빙하여 이 문제를 해결하지만, TCP 레벨 HOL blocking은 HTTP/3(QUIC)에서 완전히 해결됩니다.

Q3. mTLS가 일반 TLS와 다른 점과 서비스 메시에서의 역할을 설명하세요.

정답: 일반 TLS는 서버 인증서만 검증하지만, mTLS(Mutual TLS)는 클라이언트도 인증서를 제시하여 양방향 인증을 수행합니다.

설명: 서비스 메시(Istio 등)에서 mTLS는 사이드카 프록시(Envoy)가 자동으로 처리합니다. 각 서비스는 고유한 SPIFFE ID를 가진 X.509 인증서를 발급받아 서비스 간 통신 시 상호 인증 및 암호화가 이루어집니다. 이를 통해 내부 네트워크에서도 도청, 스푸핑, 중간자 공격을 방지할 수 있습니다.

Q4. gRPC가 REST API보다 ML 모델 서빙에 유리한 이유를 설명하세요.

정답: gRPC는 Protocol Buffers를 사용한 바이너리 직렬화, HTTP/2 기반 멀티플렉싱, 양방향 스트리밍 지원으로 ML 서빙에 최적화되어 있습니다.

설명: 대용량 텐서 데이터 전송 시 Protocol Buffers는 JSON 대비 3-5배 작은 페이로드를 생성합니다. 서버 스트리밍을 통해 LLM의 토큰 생성 결과를 실시간으로 클라이언트에 전달할 수 있습니다. 또한 .proto 스키마로 API 계약이 명확하게 정의되어 ML 파이프라인 통합 시 타입 안전성을 보장합니다.

Q5. CDN 캐시 무효화(invalidation) 전략의 종류와 트레이드오프를 설명하세요.

정답: 주요 전략으로 TTL 기반 만료, 버전 기반 URL, API를 통한 즉시 무효화, 서로게이트 키(태그) 기반 무효화가 있습니다.

설명:

  • TTL 기반: 구현이 간단하지만 만료 전까지 오래된 콘텐츠가 제공될 수 있음
  • 버전 URL (style.v2.css): 캐시 히트율 유지 + 즉각 갱신 가능, 단 URL 관리 복잡
  • 즉시 무효화 API: 빠른 반영 가능하나 CDN 비용 발생, 전파 지연(수십 초) 존재
  • 서로게이트 키: 관련 콘텐츠 그룹을 한 번에 무효화 가능 (Cloudflare Cache Tags 등)

실제 운영에서는 정적 자산(JS/CSS)은 버전 URL, API 응답은 짧은 TTL + 즉시 무효화를 조합하여 사용합니다.