19주차(테스트 심화) 이후 Claude가 임의로 구성한 학습 여정의 최종 주차.
1~19주차의 모든 코드가 사용자에게 전달되고 운영되는 영역 을 정복한다.
- HTTP/네트워크 깊이 — 백엔드의 토대
- 컨테이너와 Kubernetes — 현대 배포의 표준
- CI/CD — 코드를 사용자에게 전달하는 다리
- Observability — 운영 중 일어나는 일을 보는 눈
4년차 풀스택 개발자가 시니어/리드 로 가는 마지막 관문.
1~19주차의 위치:
왜 이 시점에 결정적인가:
ILIC 관점:
[Part A — 네트워크 기초 (Day 1-2)]
[Phase 1] HTTP 깊이 (HTTP/1.1, HTTP/2, HTTP/3)
↓
[Phase 2] TCP/IP와 TLS ◄ 정점 1
[Part B — DevOps와 컨테이너 (Day 3-4)]
[Phase 3] Docker 깊이
↓
[Phase 4] Kubernetes 핵심 ◄ 정점 2
[Part C — CI/CD와 운영 (Day 5-6)]
[Phase 5] CI/CD 파이프라인
↓
[Phase 6] 12-Factor App과 운영 원칙
[Part D — Observability (Day 7)]
[Phase 7] 3 Pillars (Logs, Metrics, Traces) ◄ 정점 3
↓
[Phase 8] 장애 대응과 사고 분석
총 8 Phase × 28 Unit — 정점 3개를 가진 압축 단일 주차.
| 영역 | 주차 | 의미 |
|---|---|---|
| Java 언어 | 1-3 | 기초 |
| 동시성 | 4 | 단일 JVM |
| Spring 생태계 | 5-10 | 프레임워크 |
| JPA | 11-12 | 영속성 |
| DB | 13-14 | 데이터 |
| Spring MVC | 15 | 웹 계층 |
| 분산 시스템 | 16-17 | 시스템 확장 |
| Spring Security | 18 | 보안 |
| 테스트 | 19 | 검증 |
| HTTP/DevOps/Observability | 20 | 운영 |
→ 시니어 백엔드 개발자가 알아야 할 거의 모든 영역
| Day | Phase | 학습 목표 |
|---|---|---|
| 1일차 | Phase 1 | HTTP 깊이 |
| 2일차 | Phase 2 | TCP/IP + TLS (★) |
| 3일차 | Phase 3 | Docker 깊이 |
| 4일차 | Phase 4 | Kubernetes (★) |
| 5일차 | Phase 5 + 6 | CI/CD + 12-Factor |
| 6일차 | Phase 7 | Observability 3 Pillars (★) |
| 7일차 | Phase 8 + 종합 | 장애 대응 + 자기 점검 |
여유 일정 (14일): 각 Part에 +1-2일. 특히 Phase 4, 7은 직접 실습 권장.
목표: 매일 쓰지만 깊이 모르는 HTTP를 정확히 이해한다.
선수 지식: 6주차 Phase 7 (웹 인프라), 15주차 (Spring MVC)
HTTP (HyperText Transfer Protocol):
"TCP 위의 텍스트 기반 요청-응답 프로토콜"
핵심 특성 ⭐ :
1. Stateless — 매 요청 독립
2. Request-Response — 클라이언트 시작
3. Text-based — 사람이 읽을 수 있음 (HTTP/2 전까지)
4. Connection-less — 응답 후 연결 종료 (기본)
HTTP 메시지 구조:
Request:
GET /api/fares/1 HTTP/1.1 ← Start Line
Host: api.ilic.com ← Headers
Accept: application/json
Authorization: Bearer eyJ...
← 빈 줄
← Body (POST/PUT 시)
Response:
HTTP/1.1 200 OK ← Status Line
Content-Type: application/json ← Headers
Content-Length: 256
Cache-Control: max-age=60
← 빈 줄
{"id": 1, "amount": 50000} ← Body
HTTP 메서드 의미 (15주차 복습 + 심화):
| 메서드 | 의미 | 멱등성 | 안전성 | Body |
|---|---|---|---|---|
| GET | 조회 | ✅ | ✅ | 권장 X |
| POST | 생성 | ❌ | ❌ | ✅ |
| PUT | 전체 수정 | ✅ | ❌ | ✅ |
| PATCH | 부분 수정 | ❌* | ❌ | ✅ |
| DELETE | 삭제 | ✅ | ❌ | 옵션 |
| HEAD | 메타데이터만 | ✅ | ✅ | X |
| OPTIONS | 메서드 확인 | ✅ | ✅ | X |
안전성(Safe): 서버 상태 변경 X
멱등성(Idempotent): 여러 번 호출해도 결과 동일
자주 헷갈리는 헤더 ⭐ :
| 헤더 | 의미 |
|---|---|
| Content-Type | 요청/응답 body의 타입 |
| Accept | 클라이언트가 받을 수 있는 타입 |
| Authorization | 인증 정보 |
| Cache-Control | 캐싱 정책 |
| ETag | 리소스 버전 식별 |
| Content-Length | body 크기 |
| Connection | 연결 유지 (keep-alive) |
| Host | 가상 호스팅용 |
| User-Agent | 클라이언트 정보 |
| Referer | 이전 페이지 |
| Set-Cookie / Cookie | 쿠키 설정/전송 |
자기 점검
선수 지식: Unit 1.1, 15주차 Phase 4
5계열 ⭐ :
| 계열 | 의미 |
|---|---|
| 1xx | 정보 (드물게 사용) |
| 2xx | 성공 |
| 3xx | 리다이렉션 |
| 4xx | 클라이언트 오류 |
| 5xx | 서버 오류 |
자주 쓰는 코드 + 흔한 오해 ⭐⭐ :
흔한 오해 ⚠️ :
흔한 오해:
흔한 오해 ⚠️ :
ILIC 시나리오 ⭐ :
사용자: 운임 견적 등록
↓
404 → 잘못된 ID
401 → 토큰 만료 → 재로그인
403 → 권한 부족
409 → 이미 등록된 견적
422 → 필수 필드 누락
429 → API 호출 한도 초과
500 → 서버 내부 버그
502 → 결제 서비스 다운
504 → DB 쿼리 타임아웃
자기 점검
선수 지식: Unit 1.2
진화 과정:
한계:
핵심 개선:
"한 연결에 여러 요청/응답을 동시에"
HTTP/1.1:
Connection 1: [Req A] → [Res A]
Connection 2: [Req B] → [Res B] (별도 연결 필요)
HTTP/2:
Connection 1: [Req A, Req B, Req C 동시]
[Res A, Res B, Res C 순서 무관]
→ HOL Blocking 해결 (Application 계층)
핵심 변화:
HTTP/2의 한계 (TCP 때문):
HTTP/3 (QUIC):
채택 현황:
비교 매트릭스 ⭐ :
| HTTP/1.1 | HTTP/2 | HTTP/3 | |
|---|---|---|---|
| 전송 방식 | 텍스트 | 바이너리 | 바이너리 |
| 멀티플렉싱 | X (HOL) | ✅ | ✅ |
| 헤더 압축 | X | HPACK | QPACK |
| 전송 계층 | TCP | TCP | QUIC (UDP) |
| TCP HOL | 있음 | 있음 | 해결 |
| 핸드셰이크 | 1-2 RTT | 1-2 RTT | 0-1 RTT |
ILIC 적용:
server:
http2:
enabled: true
자기 점검
목표: 면접 단골 — 네트워크 토대를 깊이 이해한다.
선수 지식: 컴퓨터 기초
OSI 7계층 (이론):
| 계층 | 이름 | 예 |
|---|---|---|
| 7 | Application | HTTP, FTP, SMTP |
| 6 | Presentation | TLS, SSL, JPEG |
| 5 | Session | NetBIOS |
| 4 | Transport | TCP, UDP ⭐ |
| 3 | Network | IP, ICMP ⭐ |
| 2 | Data Link | Ethernet, Wi-Fi |
| 1 | Physical | 케이블, 광섬유 |
TCP/IP 4계층 (실제):
| 계층 | OSI 매핑 |
|---|---|
| Application | 7-5 |
| Transport | 4 |
| Internet | 3 |
| Network Access | 2-1 |
핵심 캡슐화 ⭐ :
[HTTP Request 데이터]
↓ Application Layer
[HTTP + 데이터]
↓ Transport Layer (TCP)
[TCP 헤더 + HTTP + 데이터] = TCP 세그먼트
↓ Internet Layer (IP)
[IP 헤더 + TCP 세그먼트] = IP 패킷
↓ Network Access
[Ethernet 헤더 + IP 패킷] = Frame
↓
[전송]
자기 점검
선수 지식: Unit 2.1
TCP:
"연결 지향적, 신뢰성 있는 전송 프로토콜"
3-way Handshake — 연결 수립 ⭐ :
Client Server
| |
|─── SYN (seq=x) ─────────→| 1. "연결할게"
| |
|←── SYN-ACK (seq=y, ─────| 2. "OK, 너도 OK?"
| ack=x+1) |
| |
|─── ACK (ack=y+1) ───────→| 3. "OK"
| |
|═══ 연결 수립 ═══════════|
| |
|─── HTTP Request ────────→|
|←── HTTP Response ────────|
왜 3번? ⭐ :
4-way Handshake — 연결 종료 ⭐ :
Client Server
| |
|─── FIN ─────────────────→| 1. "끝낼게"
| |
|←── ACK ──────────────────| 2. "OK"
| |
| (서버 작업 마무리) |
| |
|←── FIN ──────────────────| 3. "나도 끝"
| |
|─── ACK ─────────────────→| 4. "OK"
| |
|═══ TIME_WAIT (Client) ══|
TIME_WAIT ⭐ :
TIME_WAIT 해결:
SO_REUSEADDR 옵션TCP의 신뢰성 보장 메커니즘 ⭐ :
TCP vs UDP ⭐ :
| TCP | UDP | |
|---|---|---|
| 연결 | 연결 지향 | 비연결 |
| 신뢰성 | ✅ | X |
| 순서 | 보장 | 보장 X |
| 속도 | 느림 | 빠름 |
| Header | 20+ bytes | 8 bytes |
| 용도 | HTTP, DB | DNS, 게임, VoIP |
면접 모의 답변 ⭐ :
"TCP는 연결 지향적이고 신뢰성 있는 전송 프로토콜입니다. 연결 수립 시 3-way handshake를 거치는데, SYN, SYN-ACK, ACK 세 번의 메시지로 양쪽 모두의 송수신 능력을 확인합니다.
종료 시에는 4-way handshake로, 양쪽이 각각 FIN과 ACK를 주고받습니다. 이때 마지막 ACK 손실에 대비해 TIME_WAIT 상태로 일정 시간 대기하는데, 이게 누적되면 소켓 고갈 문제가 발생할 수 있어 SO_REUSEADDR 옵션이나 Connection Pool로 해결합니다.
TCP는 신뢰성을 위해 Sequence Number, ACK, Checksum, Flow Control, Congestion Control을 사용합니다. 이런 메커니즘 때문에 UDP보다 느리지만, 정확성이 중요한 HTTP, DB 통신 등에 사용됩니다."
자기 점검
선수 지식: Unit 2.2, 18주차 (Security)
HTTPS:
"HTTP + TLS — 암호화된 HTTP"
왜 필요한가:
TLS (Transport Layer Security):
TLS 1.2 Handshake (전통):
Client Server
| |
|─── ClientHello ─────────────────→| 지원 암호 알고리즘
| |
|←── ServerHello ──────────────────| 선택한 알고리즘
|←── Certificate (인증서) ─────────| 서버 인증서
|←── ServerHelloDone ──────────────|
| |
| [인증서 검증] |
| |
|─── ClientKeyExchange ───────────→| Pre-Master Secret 전송
|─── ChangeCipherSpec ────────────→|
|─── Finished ────────────────────→|
| |
|←── ChangeCipherSpec ─────────────|
|←── Finished ─────────────────────|
| |
|═══ 암호화된 통신 ════════════════|
RTT: 2 RTT (왕복 2번)
TLS 1.3 Handshake (2018) ⭐ :
Client Server
| |
|─── ClientHello + Key Share ─────→|
| |
|←── ServerHello + Cert + Key + ──|
| Finished |
| |
|─── Finished ────────────────────→|
| |
|═══ 암호화된 통신 ════════════════|
RTT: 1 RTT
0-RTT 모드: 이전 세션 재개 시 즉시
개선점:
핵심 원리 ⭐ :
왜 둘 다?:
인증서 체인:
Root CA (브라우저에 내장)
↓ 서명
Intermediate CA
↓ 서명
Leaf Certificate (실제 사이트)
Let's Encrypt:
HTTPS 비용 ⚠️ :
현대 결론:
ILIC 적용:
자기 점검
선수 지식: Unit 2.3
DNS (Domain Name System):
"도메인 이름 ↔ IP 주소 변환"
api.ilic.com → 203.0.113.42
DNS 조회 흐름:
1. Browser Cache 확인
2. OS Cache 확인
3. Router Cache 확인
4. ISP DNS 서버 조회
5. Root → TLD → Authoritative DNS
DNS 레코드 타입 ⭐ :
| 타입 | 의미 |
|---|---|
| A | 도메인 → IPv4 |
| AAAA | 도메인 → IPv6 |
| CNAME | 도메인 → 다른 도메인 (별칭) |
| MX | 메일 서버 |
| TXT | 텍스트 (SPF, DKIM, 인증) |
| NS | 네임서버 |
| SRV | 서비스 (포트 포함) |
Load Balancing ⭐ :
역할:
알고리즘:
| 알고리즘 | 특징 |
|---|---|
| Round Robin | 순차 분배 |
| Least Connections | 연결 수 적은 곳 |
| IP Hash | 같은 IP는 같은 서버 (Sticky) |
| Weighted | 가중치 기반 |
Layer 4 vs Layer 7 LB ⭐ :
| L4 (Transport) | L7 (Application) | |
|---|---|---|
| 기준 | IP, Port | URL, Header, Cookie |
| 속도 | 빠름 | 느림 |
| 기능 | 단순 | 풍부 (Path 라우팅 등) |
| 예 | AWS NLB | AWS ALB, Nginx |
ILIC 시나리오:
/api/* → Spring Boot/static/* → CDNSticky Session ⚠️ :
자기 점검
목표: ILIC가 이미 사용 중일 가능성 높지만 깊이 정리한다.
선수 지식: 운영체제 기초
전통 배포의 문제:
가상화의 진화:
[App A] [App B] [App C]
[Guest OS] [Guest OS] [Guest OS] ← 무거움
[Hypervisor]
[Host OS]
[Hardware]
[App A] [App B] [App C]
[Bin/Lib] [Bin/Lib] [Bin/Lib]
[Container Runtime (Docker)]
[Host OS] ← 공유
[Hardware]
핵심 차이:
Docker의 핵심 메커니즘 ⭐ :
3가지 핵심 개념 ⭐ :
자기 점검
선수 지식: Unit 3.1
기본 Dockerfile (Spring Boot):
FROM openjdk:17-jdk-slim
WORKDIR /app
COPY target/ilic.jar app.jar
EXPOSE 8080
CMD ["java", "-jar", "app.jar"]
문제:
Multi-stage Build ⭐ — 빌드와 실행 분리:
# 빌드 스테이지
FROM gradle:8-jdk17 AS builder
WORKDIR /build
COPY . .
RUN gradle build -x test
# 실행 스테이지
FROM openjdk:17-jre-slim
WORKDIR /app
COPY --from=builder /build/libs/ilic.jar app.jar
EXPOSE 8080
CMD ["java", "-jar", "app.jar"]
효과:
레이어 캐싱 활용 ⭐ :
나쁜 예:
COPY . .
RUN gradle build # 코드 1줄 변경해도 처음부터
좋은 예 (Spring Boot 권장):
# 의존성만 먼저
COPY build.gradle settings.gradle ./
RUN gradle dependencies
# 그 다음 소스
COPY src ./src
RUN gradle build
→ 의존성 변경 없으면 캐시 활용
Spring Boot Layered JAR ⭐ :
Spring Boot 2.3+가 제공하는 최적화:
FROM openjdk:17-jre-slim
WORKDIR /app
ARG JAR_FILE=target/ilic.jar
COPY ${JAR_FILE} app.jar
RUN java -Djarmode=layertools -jar app.jar extract
FROM openjdk:17-jre-slim
WORKDIR /app
COPY --from=builder /app/dependencies/ ./
COPY --from=builder /app/spring-boot-loader/ ./
COPY --from=builder /app/snapshot-dependencies/ ./
COPY --from=builder /app/application/ ./
ENTRYPOINT ["java", "org.springframework.boot.loader.JarLauncher"]
효과:
보안 모범 사례 ⭐ :
RUN useradd -ms /bin/bash appuser
USER appuser
alpine 또는 slim 변형distroless (Google) — 더 작음.git
.gradle
target
*.md
FROM openjdk:17.0.9-jre-slim # ❌ latest 금지
ILIC 시나리오:
자기 점검
선수 지식: Unit 3.2
문제:
Docker Compose:
"여러 컨테이너를 YAML로 정의 + 한 번에 실행"
version: '3.8'
services:
app:
build: .
ports:
- "8080:8080"
environment:
- SPRING_DATASOURCE_URL=jdbc:mysql://mysql:3306/ilic
- SPRING_DATASOURCE_PASSWORD=${DB_PASSWORD}
depends_on:
mysql:
condition: service_healthy
redis:
condition: service_started
networks:
- ilic-network
mysql:
image: mysql:8.0
environment:
MYSQL_ROOT_PASSWORD: ${DB_PASSWORD}
MYSQL_DATABASE: ilic
volumes:
- mysql-data:/var/lib/mysql
healthcheck:
test: ["CMD", "mysqladmin", "ping"]
interval: 10s
timeout: 5s
retries: 5
networks:
- ilic-network
redis:
image: redis:7-alpine
networks:
- ilic-network
kafka:
image: confluentinc/cp-kafka:latest
networks:
- ilic-network
volumes:
mysql-data:
networks:
ilic-network:
실행:
docker-compose up -d
docker-compose down
docker-compose logs -f app
Compose vs Kubernetes:
ILIC 활용:
자기 점검
목표: 현대 운영의 표준 — K8s의 핵심 컨셉을 면접 답변 가능 수준으로.
선수 지식: Phase 3
Docker만으로의 한계:
Kubernetes (K8s):
"컨테이너 오케스트레이션 표준"
핵심 기능 ⭐ :
Cluster 구조 ⭐ :
[Control Plane (Master)]
├── API Server ← 모든 명령의 입구
├── etcd ← 상태 저장 (17주차 합의 알고리즘)
├── Scheduler ← Pod 배치 결정
└── Controller Manager ← 상태 유지
[Worker Nodes]
├── Node 1
│ ├── kubelet ← 노드 에이전트
│ ├── kube-proxy ← 네트워크
│ └── Pods (containers)
├── Node 2
└── Node 3
자기 점검
선수 지식: Unit 4.1
3가지 핵심 리소스 ⭐ :
apiVersion: v1
kind: Pod
metadata:
name: ilic-pod
spec:
containers:
- name: ilic-app
image: ilic:1.0.0
ports:
- containerPort: 8080
특징:
왜 Pod인가 (그냥 컨테이너 아니고):
apiVersion: apps/v1
kind: Deployment
metadata:
name: ilic-deployment
spec:
replicas: 3 ← 3개 Pod
selector:
matchLabels:
app: ilic
template:
metadata:
labels:
app: ilic
spec:
containers:
- name: ilic-app
image: ilic:1.0.0
resources:
requests:
memory: "512Mi"
cpu: "500m"
limits:
memory: "1Gi"
cpu: "1000m"
역할 ⭐ :
Rolling Update 흐름:
v1: [Pod1] [Pod2] [Pod3]
↓
v2 시작: [Pod1] [Pod2] [Pod3] [Pod4(v2)]
↓
[Pod2] [Pod3] [Pod4(v2)] [Pod5(v2)] ← Pod1 종료
↓
[Pod4(v2)] [Pod5(v2)] [Pod6(v2)]
문제: Pod IP는 변함 → 어떻게 접근?
Service:
"Pod 그룹의 안정적인 접근점"
apiVersion: v1
kind: Service
metadata:
name: ilic-service
spec:
selector:
app: ilic ← 이 라벨의 Pod 묶음
ports:
- port: 80
targetPort: 8080
type: ClusterIP
Service Type ⭐ :
| Type | 의미 |
|---|---|
| ClusterIP | 클러스터 내부만 (기본) |
| NodePort | 노드의 특정 포트로 노출 |
| LoadBalancer | 외부 LB 자동 생성 (AWS ELB 등) |
| ExternalName | DNS CNAME |
Service Discovery (17주차 자동 해결):
http://ilic-service 으로 접근 → K8s가 자동 라우팅ILIC 시나리오:
# Backend
Deployment: ilic-app (replicas: 3)
Service: ilic-service (ClusterIP)
# Frontend
Deployment: ilic-vue (replicas: 2)
Service: ilic-vue-service
# Ingress (외부 접근)
Ingress:
- api.ilic.com → ilic-service
- ilic.com → ilic-vue-service
자기 점검
선수 지식: Unit 4.2
문제: LoadBalancer Service를 도메인마다 만들면 비용 ↑
Ingress:
"HTTP/HTTPS L7 라우팅"
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: ilic-ingress
annotations:
cert-manager.io/cluster-issuer: letsencrypt
spec:
tls:
- hosts:
- api.ilic.com
secretName: ilic-tls
rules:
- host: api.ilic.com
http:
paths:
- path: /api
pathType: Prefix
backend:
service:
name: ilic-service
port:
number: 80
- path: /admin
pathType: Prefix
backend:
service:
name: ilic-admin-service
port:
number: 80
역할:
apiVersion: v1
kind: ConfigMap
metadata:
name: ilic-config
data:
application.yml: |
spring:
profiles:
active: prod
server:
port: 8080
사용:
spec:
containers:
- name: ilic-app
volumeMounts:
- name: config
mountPath: /config
volumes:
- name: config
configMap:
name: ilic-config
apiVersion: v1
kind: Secret
metadata:
name: ilic-secret
type: Opaque
data:
db-password: cGFzc3dvcmQ= # Base64
사용:
env:
- name: DB_PASSWORD
valueFrom:
secretKeyRef:
name: ilic-secret
key: db-password
⚠️ 주의:
ILIC 적용:
자기 점검
선수 지식: Unit 4.2
3가지 Probe:
spec:
containers:
- name: ilic-app
livenessProbe: ← "살아있나?"
httpGet:
path: /actuator/health
port: 8080
initialDelaySeconds: 30
periodSeconds: 10
failureThreshold: 3
readinessProbe: ← "트래픽 받을 준비?"
httpGet:
path: /actuator/health/readiness
port: 8080
periodSeconds: 5
startupProbe: ← "시작 중?"
httpGet:
path: /actuator/health
port: 8080
failureThreshold: 30
periodSeconds: 10
차이점 ⭐ :
| Liveness | Readiness | Startup | |
|---|---|---|---|
| 실패 시 | 재시작 | 트래픽 차단 | 다른 Probe 시작 |
| 용도 | Deadlock 감지 | 일시적 부하 | 느린 시작 |
Spring Boot Actuator 활용:
management:
endpoints:
web:
exposure:
include: health
endpoint:
health:
probes:
enabled: true
→ /actuator/health/liveness, /actuator/health/readiness 자동 제공
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
name: ilic-hpa
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: ilic-deployment
minReplicas: 3
maxReplicas: 10
metrics:
- type: Resource
resource:
name: cpu
target:
type: Utilization
averageUtilization: 70
효과:
ILIC 시나리오:
VPA (Vertical) vs HPA (Horizontal) ⭐ :
자기 점검
목표: 코드를 사용자에게 자동으로 전달하는 다리를 이해한다.
선수 지식: Phase 4, 19주차 (테스트)
CI (Continuous Integration):
"자주 통합 — 빌드 + 테스트 자동화"
CD (Continuous Delivery / Deployment):
전형적 파이프라인 ⭐ :
[Code Push]
↓
[Build] ← Gradle build
↓
[Static Analysis] ← SonarQube, Checkstyle
↓
[Unit Test] ← JUnit (19주차)
↓
[Integration Test] ← Testcontainers (19주차)
↓
[Build Image] ← Docker
↓
[Push to Registry] ← ECR, GCR
↓
[Deploy to Dev] ← 자동
↓
[E2E Test] ← 자동
↓
[Deploy to Staging] ← 자동
↓
[Manual Approval] ← 운영 배포 전
↓
[Deploy to Prod]
↓
[Smoke Test]
각 단계의 의미 ⭐ :
| 단계 | 목적 |
|---|---|
| Build | 코드 → 실행 가능 형태 |
| Static Analysis | 컴파일 전에 잡기 (코드 스멜, 보안) |
| Unit Test | 빠른 회귀 검증 |
| Integration Test | 통합 검증 |
| Build Image | 배포 단위 |
| Deploy to Dev | 개발자 검증 |
| E2E Test | 전체 시나리오 |
| Deploy to Staging | 운영과 동일 환경 |
| Manual Approval | 위험 통제 |
| Deploy to Prod | 사용자 노출 |
자기 점검
선수 지식: Unit 5.1
GitHub Actions (가장 보편):
.github/workflows/ci-cd.yml:
name: CI/CD
on:
push:
branches: [main, develop]
pull_request:
branches: [main]
jobs:
test:
runs-on: ubuntu-latest
services:
mysql:
image: mysql:8.0
env:
MYSQL_ROOT_PASSWORD: test
MYSQL_DATABASE: test
ports:
- 3306:3306
steps:
- uses: actions/checkout@v4
- name: Setup JDK 17
uses: actions/setup-java@v4
with:
java-version: '17'
distribution: 'temurin'
- name: Cache Gradle
uses: actions/cache@v3
with:
path: |
~/.gradle/caches
~/.gradle/wrapper
key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle*') }}
- name: Run Tests
run: ./gradlew test
- name: Generate Coverage Report
run: ./gradlew jacocoTestReport
- name: Upload Coverage to Codecov
uses: codecov/codecov-action@v3
build-and-push:
needs: test
if: github.ref == 'refs/heads/main'
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Build with Gradle
run: ./gradlew build -x test
- name: Build Docker Image
run: docker build -t ilic:${{ github.sha }} .
- name: Login to ECR
uses: aws-actions/amazon-ecr-login@v2
- name: Push Image
run: docker push ${{ env.ECR_REGISTRY }}/ilic:${{ github.sha }}
deploy:
needs: build-and-push
runs-on: ubuntu-latest
steps:
- name: Deploy to K8s
run: kubectl set image deployment/ilic ilic=ilic:${{ github.sha }}
핵심 개념:
needs로 의존성캐싱 ⭐ — 속도 향상:
자기 점검
선수 지식: Phase 4, Unit 5.2
3가지 배포 전략 ⭐ :
v1: [Pod] [Pod] [Pod]
↓ 1개씩 교체
v2: [Pod(v2)] [Pod(v1)] [Pod(v1)]
↓
v2: [Pod(v2)] [Pod(v2)] [Pod(v1)]
↓
v2: [Pod(v2)] [Pod(v2)] [Pod(v2)]
장점: 단순, 자원 효율
단점: 롤백 느림, 두 버전 공존
Blue (운영): [Pod(v1)] [Pod(v1)] [Pod(v1)] ← 트래픽
Green (대기): [Pod(v2)] [Pod(v2)] [Pod(v2)]
↓ 트래픽 전환
Blue (대기): [Pod(v1)] [Pod(v1)] [Pod(v1)]
Green (운영): [Pod(v2)] [Pod(v2)] [Pod(v2)] ← 트래픽
장점: 즉시 롤백 가능, 두 버전 공존 X
단점: 자원 2배
v1: [Pod] [Pod] [Pod] [Pod] [Pod] ← 95% 트래픽
v2: [Pod] ← 5% 트래픽
↓ 점진적 확대
v1: [Pod] [Pod] [Pod]
v2: [Pod] [Pod] ← 50% 트래픽
↓
v1:
v2: [Pod] [Pod] [Pod] [Pod] [Pod] ← 100% 트래픽
장점: 점진적 검증, 실패 시 적은 영향
단점: 복잡
Argo Rollouts, Flagger 등 도구 활용.
선택 가이드 ⭐ :
| 상황 | 추천 |
|---|---|
| 일반 배포 | Rolling |
| 빠른 롤백 필요 | Blue-Green |
| 위험한 변경, 점진 검증 | Canary |
| 자원 여유 적음 | Rolling |
| 완전 격리 필요 | Blue-Green |
ILIC 시나리오:
자기 점검
목표: 클라우드 네이티브의 표준 원칙을 정리한다.
선수 지식: Phase 5
12-Factor:
"클라우드 네이티브 앱의 12가지 원칙" (Heroku 발표)
12가지 원칙 요약 ⭐ :
| # | 원칙 | 의미 |
|---|---|---|
| 1 | Codebase | 한 앱 = 한 저장소 |
| 2 | Dependencies | 명시적 선언 (build.gradle) |
| 3 | Config | 환경변수로 분리 |
| 4 | Backing Services | DB/Redis 등 = 외부 자원 |
| 5 | Build/Release/Run | 단계 분리 |
| 6 | Processes | Stateless |
| 7 | Port Binding | 자체 포트 노출 |
| 8 | Concurrency | 프로세스 단위 확장 |
| 9 | Disposability | 빠른 시작/종료 (Graceful Shutdown) |
| 10 | Dev/Prod Parity | 환경 동일성 |
| 11 | Logs | stdout으로 출력 (Phase 7) |
| 12 | Admin Processes | 일회성 작업 분리 |
핵심 강조 ⭐ :
// Spring Boot
server:
shutdown: graceful
spring:
lifecycle:
timeout-per-shutdown-phase: 30s
→ SIGTERM 받으면 새 요청 안 받고, 진행 중인 요청 완료 후 종료.
ILIC 점검:
자기 점검
목표: 운영 중 일어나는 일을 보는 세 가지 눈을 정복한다.
선수 지식: Phase 5-6
Monitoring (전통):
Observability (현대):
관계:
"Monitoring은 Observability의 부분 집합"
3 Pillars ⭐⭐⭐ :
| Pillar | 답하는 질문 | 도구 |
|---|---|---|
| Logs | 무슨 일이 일어났는가? | ELK, Loki |
| Metrics | 얼마나 일어났는가? | Prometheus, Grafana |
| Traces | 어떻게 흘렀는가? | Zipkin, Jaeger |
→ 17주차 Distributed Tracing이 Traces.
자기 점검
선수 지식: Unit 7.1
나쁜 로그:
2024-05-04 10:00:00 - 운임 처리 시작
2024-05-04 10:00:05 - 알 수 없는 오류 발생
문제:
구조화 로깅 (JSON) ⭐ :
{
"timestamp": "2024-05-04T10:00:00Z",
"level": "ERROR",
"service": "ilic-fare-service",
"trace_id": "abc123",
"user_id": "42",
"fare_id": "100",
"error": "PaymentTimeout",
"duration_ms": 5000,
"message": "결제 처리 시간 초과"
}
효과:
Spring Boot 구조화 로깅:
# logback-spring.xml
<configuration>
<appender name="JSON" class="ch.qos.logback.core.ConsoleAppender">
<encoder class="net.logstash.logback.encoder.LogstashEncoder">
<providers>
<timestamp/>
<logLevel/>
<loggerName/>
<mdc/>
<message/>
</providers>
</encoder>
</appender>
<root level="INFO">
<appender-ref ref="JSON"/>
</root>
</configuration>
MDC (Mapped Diagnostic Context) ⭐ — 컨텍스트 전파:
@Component
public class TraceIdFilter extends OncePerRequestFilter {
@Override
protected void doFilterInternal(...) {
String traceId = request.getHeader("X-Trace-Id");
if (traceId == null) traceId = UUID.randomUUID().toString();
MDC.put("trace_id", traceId);
try {
filterChain.doFilter(request, response);
} finally {
MDC.clear();
}
}
}
→ 모든 로그에 trace_id 자동 포함
ELK Stack (전통):
Grafana Loki (현대):
ILIC 적용:
자기 점검
선수 지식: Unit 7.2
Metrics:
"시계열 숫자 데이터 — 얼마나 일어났는가"
예:
Prometheus — 사실상 표준:
Pull 모델:
/actuator/prometheus 노출# Spring Boot
management:
endpoints:
web:
exposure:
include: prometheus
→ /actuator/prometheus 자동 제공:
# HELP http_server_requests_seconds
# TYPE http_server_requests_seconds histogram
http_server_requests_seconds_count{method="GET",uri="/api/fares"} 1234
http_server_requests_seconds_sum{method="GET",uri="/api/fares"} 45.6
4가지 메트릭 타입 ⭐ :
| 타입 | 의미 | 예 |
|---|---|---|
| Counter | 누적 카운트 (감소 X) | 총 요청 수 |
| Gauge | 현재 값 | 현재 메모리 |
| Histogram | 분포 (버킷) | 응답 시간 분포 |
| Summary | Percentile | P95 응답 시간 |
Spring Boot Actuator + Micrometer ⭐ :
implementation 'io.micrometer:micrometer-registry-prometheus'
자동 메트릭 :
커스텀 메트릭:
@Service
public class FareService {
private final MeterRegistry meterRegistry;
private final Counter fareCounter;
private final Timer fareTimer;
public FareService(MeterRegistry registry) {
this.fareCounter = registry.counter("fare.created.total");
this.fareTimer = registry.timer("fare.processing");
}
public Fare create(FareRequest request) {
return fareTimer.record(() -> {
Fare fare = ...;
fareCounter.increment();
return fare;
});
}
}
Grafana — 시각화:
SLI/SLO/SLA ⭐ — 면접 단골:
| 의미 | 예 | |
|---|---|---|
| SLI (Indicator) | 측정 지표 | "에러율" |
| SLO (Objective) | 목표 | "에러율 < 0.1%" |
| SLA (Agreement) | 계약 | "99.9% 가용성, 못 지키면 환불" |
ILIC 권장:
자기 점검
선수 지식: Unit 7.3, 17주차 Phase 9.4
복습 (17주차):
OpenTelemetry (OTel) ⭐ :
"관측 가능성의 표준 — Logs/Metrics/Traces 통합"
채택 현황:
Spring Boot 통합 (Micrometer Tracing):
implementation 'org.springframework.boot:spring-boot-starter-actuator'
implementation 'io.micrometer:micrometer-tracing-bridge-otel'
implementation 'io.opentelemetry:opentelemetry-exporter-otlp'
management:
tracing:
sampling:
probability: 0.1 # 10% 샘플링
otlp:
tracing:
endpoint: http://otel-collector:4317
→ 자동으로 모든 HTTP 요청, DB 쿼리, Redis 호출 등 추적
3 Pillars 통합 ⭐ — 가장 강력:
// Log
{
"timestamp": "...",
"level": "ERROR",
"trace_id": "abc123", ← Trace와 연결
"span_id": "span456",
"message": "결제 실패"
}
효과:
ILIC 적용:
자기 점검
목표: 시니어가 아닌 4년차의 결정적 차이 영역.
선수 지식: Phase 7
On-Call 문화:
좋은 알림 시스템 ⭐ :
| 단계 | 의미 |
|---|---|
| Info | 참고용 (이메일) |
| Warning | 주시 (Slack) |
| Critical | 즉시 대응 (전화/SMS) |
알림 피로(Alert Fatigue) ⚠️ :
장애 대응 흐름 ⭐ :
1. 감지 (Detection)
- 자동 알림
- 사용자 신고
↓
2. 분류 (Triage)
- 심각도 판단
- 영향 범위
↓
3. 완화 (Mitigation) — 우선!
- 빠른 해결 (롤백, 트래픽 차단)
- "수정" 보다 "중단" 이 우선
↓
4. 복구 (Recovery)
- 정상 상태로
↓
5. 분석 (Post-mortem) ⭐
- 사후 분석
- 비난 X, 학습 ○
핵심 원칙 ⭐ :
"근본 원인 수정 전에 영향 완화 우선"
예: 결제 시스템 다운 → 디버깅 X → 즉시 롤백
의사결정 프레임워크:
시니어의 차별화:
자기 점검
선수 지식: Unit 8.1
Postmortem:
"장애 후 공식적인 분석 + 학습 문서"
핵심 원칙 ⭐ :
"Blameless (비난 없는)"
Postmortem 템플릿 ⭐ :
# 장애 보고서 — 운임 시스템 다운 (2024-05-04)
## 요약
- 발생: 2024-05-04 14:00 KST
- 복구: 2024-05-04 15:30 KST
- 영향 범위: 전체 운임 시스템
- 영향받은 사용자: 약 500명
## Timeline
- 14:00 — DB 마이그레이션 시작
- 14:15 — 마이그레이션 완료, but 인덱스 누락
- 14:20 — 운임 검색 API 응답 30초 → Critical 알림
- 14:30 — 온콜 인지
- 14:45 — 마이그레이션 롤백 결정
- 15:00 — 롤백 시작
- 15:30 — 정상 복구
## 근본 원인 (Root Cause)
- 마이그레이션 스크립트에 인덱스 생성 누락
- Staging에서 충분한 부하 테스트 없었음
- → 단일 원인 X, 여러 요인의 결합
## 무엇이 잘 되었나
- 빠른 알림 감지 (5분)
- 신속한 롤백 결정
## 무엇이 잘못되었나
- 마이그레이션 검토 부재
- Staging의 데이터 양이 운영과 다름
## Action Items
1. 마이그레이션 PR 템플릿 도입 (Owner: A, Due: 5/15)
2. Staging 데이터 양을 운영의 30%로 (Owner: B, Due: 5/30)
3. 마이그레이션 시 자동 부하 테스트 (Owner: C, Due: 6/15)
좋은 Postmortem의 특징:
1. 사실 위주 — 추측 X
2. Blameless — 사람 비난 X
3. Action Items 명확 — 누가, 언제까지
4. 공유 — 팀 전체 학습
ILIC 적용:
면접 질문 대비 ⭐ :
"지금까지 가장 어려웠던 장애 경험은?"
좋은 답변 구조:
1. 상황 (간단)
2. 문제 발견 과정
3. 완화 시도
4. 근본 원인
5. 학습 + 개선
자기 점검
★★★ 면접 단골 (반드시):
★★ 매우 권장:
Phase 2 (TCP/TLS) — 백엔드의 토대. "TCP 3-way handshake 설명?" 면접 100%. 면접관이 깊이 파고드는 질문 영역.
Phase 4 (Kubernetes) — 현대 운영의 표준. "K8s 써보셨나요?" 시 Pod/Deployment/Service의 차이를 1분 안에 답할 수 있어야 차별화.
Phase 7 (Observability) — 시니어의 결정적 차별화. "장애를 어떻게 찾으세요?" 답변에 3 Pillars + SLI/SLO 들어가면 시니어 레벨.
이제 시니어 백엔드 개발자가 알아야 할 거의 모든 영역을 학습했습니다:
| 영역 | 주차 | 깊이 |
|---|---|---|
| Java 언어 | 1-3 | ★★★ |
| 동시성 | 4 | ★★★ |
| Spring Core | 5, 8-9 | ★★★ |
| 트랜잭션 | 7, 10 | ★★★ |
| JPA | 11-12 | ★★★ |
| DB | 13-14 | ★★★ |
| Spring MVC | 15 | ★★★ |
| 분산 시스템 | 16-17 | ★★★ |
| Spring Security | 18 | ★★★ |
| 테스트 | 19 | ★★★ |
| HTTP/DevOps/Observability | 20 | ★★★ |
→ 20주에 걸쳐 자바 백엔드 시니어 개발자의 거의 모든 영역을 정복
가장 중요합니다. 20주차 분량의 이론 위에:
이 작업이 이론을 면접 합격으로 변환 합니다.
1~20주차를 완전히 체화했다면:
현재의 학습으로 부족한 영역 (자기 학습 권장):
보완 필요: