HTTP 개요
- 거의 모든 형태의 데이터를 전송할 수 있다.
- TCP 기반의 HTTP/1.1 이 주로 쓰인다.
비 연결성 (Connectionless)
- HTTP 는 기본적으로 연결을 유지하지 않는 모델이다.
- 요청 → 응답 → 바로 연결 끊음
- 서버 자원을 매우 효율적으로 사용할 수 있다.
한계와 극복
- TCP/IP 연결을 매번 새로 맺어야 하기 때문에 3 way handshake 시간이 추가적으로 발생한다.
- 지금은 HTTP Persistent Connections 로 문제를 해결한다.
HTTP 와 HTTPS 의 차이
- HTTPS 는 HTTP 에 보안 계층을 추가한 것이다.
- HTTPS 는 제 3자 인증, 공개키 암호화, 비밀키 암호화를 사용한다.
인증서 파일 형식
| 파일 | 내용 | 비공개 여부 |
|---|---|---|
.key | 개인키 (Private Key) | 🔒 비공개 — 서버만 보관 |
.csr | 공개키 + 도메인 정보 (Certificate Signing Request) | 일시적 — 발급 후 불필요 |
.crt | 공개키 + 메타정보 + CA 디지털 서명 (Certificate) | 공개 |
server.key (개인키)
-----BEGIN RSA PRIVATE KEY-----
MIIEpAIBAAKCAQEA7SVFkaRk4c8qDK0v
JpjblBKEgW0k5Cp/qkLqqR0IGGwH8QBO
... (Base64 인코딩된 RSA 개인키 데이터) ...
tSsQev5eoHqdPb4DCUS5J1LyFC1GJVBj
x0g4puCkTMfbXBJKbFoJfy8=
-----END RSA PRIVATE KEY-----
- RSA 2048-bit 이상 권장
- 서버에서만 보관하며 절대 외부에 공유하지 않는다
- 파일 권한:
chmod 600 server.key
server.csr (인증서 서명 요청서)
-----BEGIN CERTIFICATE REQUEST-----
MIICYTCCAUkCAQAwHDEaMBgGA1UEAwwR
ZXhhbXBsZS5jb20wggEiMA0GCSqGSIb3
... (Base64 인코딩된 CSR 데이터) ...
WlFaVFJ2emswN2MxTlJrbzc3MDVYQi9s
-----END CERTIFICATE REQUEST-----
openssl req -text -noout -in server.csr로 열어보면:
Subject: CN=example.com
Public Key Algorithm: rsaEncryption
RSA Public Key: (2048 bit)
Modulus:
00:ab:cd:ef:12:34:56:78:9a:bc:de:f0:...
Exponent: 65537 (0x10001)
- 서버의 공개키 + 도메인 정보를 담은 CA 서명 요청서이다
- 서명 요청 후에는 더 이상 필요 없다
server.crt (인증서)
-----BEGIN CERTIFICATE-----
MIIDXTCCAkWgAwIBAgIJAJC1HiIAZAiU
MA0GCSqGSIb3DqEBCwUAMBwxGjAYBgNV
... (Base64 인코딩된 인증서 데이터) ...
Fd4DCBh8+4OArv+12NUE3LcVB6LDY
-----END CERTIFICATE-----
openssl x509 -text -noout -in server.crt로 열어보면:
Issuer: CN=My CA ← 서명한 CA 정보
Subject: CN=example.com ← 이 인증서의 주체 (도메인)
Validity:
Not Before: Mar 22 00:00:00 2026 GMT ← 유효기간 시작
Not After: Mar 22 00:00:00 2027 GMT ← 유효기간 만료
Public Key Algorithm: rsaEncryption
RSA Public Key: (2048 bit) ← 서버 공개키
Modulus:
00:ab:cd:ef:12:34:56:78:...
Exponent: 65537 (0x10001)
Signature Algorithm: sha256WithRSAEncryption ← 서명 알고리즘
Signature Value: ← CA 서명값 (hash를 ca.key로 암호화)
3a:cb:99:7f:2e:1a:b4:d9:...
.crt의 내용은 평문이며,Signature Value만ca.key로 암호화된 hash 이다.crt= 서버 공개키 + 메타정보 (도메인, 유효기간, 발급자) + CA 의 디지털 서명
인증서 발급 과정
# 1. 개인키 생성
openssl genrsa -out server.key 2048
# 2. 개인키에서 공개키를 추출하고 도메인 정보를 합쳐 CSR 생성
openssl req -new -key server.key -out server.csr -subj "/CN=example.com"
# 3. CA 가 CSR 을 받아서 ca.key 로 서명하여 server.crt 발급
openssl x509 -req -in server.csr -CA ca.crt -CAkey ca.key -out server.crt
# 발급된 인증서 내용 확인
openssl x509 -in server.crt -text -nooutsequenceDiagram participant CA as CA 서버 participant S as 서버 participant C as 클라이언트 Note over S, CA: 1단계 — 인증서 발급 (TLS 통신 이전, 1회성) S->>S: openssl genrsa -out server.key 2048<br/>→ 개인키 생성 S->>S: openssl req -new -key server.key -out server.csr<br/>→ 공개키 추출 + 도메인 정보 → CSR 생성 S->>CA: server.csr 제출 (공개키 + 도메인 정보) CA->>CA: hash(CSR 내용) → ca.key 로 RSA 암호화 → Signature Value 생성 CA->>CA: CSR 내용 + Signature Value + 유효기간 → server.crt 발급 CA->>S: server.crt 전달 S->>S: 웹 서버에 인증서 설정<br/>(Nginx ssl_certificate, ssl_certificate_key 지시어) Note over C: 2단계 — ca.crt 는 이미 클라이언트에 존재 Note over C: 공인 CA → OS/브라우저 Trust Store 에 사전 설치<br/>사설 CA → 관리자가 수동 배포
- CA (Certificate Authority) 는 인증서 발급 시점에만 관여하고, TLS 핸드셰이크 시점에는 참여하지 않는다.
디지털 서명과 검증 원리
- 서명(sign) ≠ 암호화(encrypt) —
.crt인증서의 내용은 평문이며, CA 서명은 “이 내용이 변조되지 않았고 CA 가 보증한다”는 증명이다.
flowchart LR subgraph "CA 가 서명할 때 (ca.key 사용)" A["server.crt 평문 데이터<br/>Issuer: CN=My CA<br/>Subject: CN=example.com<br/>Public Key: 00:ab:cd:ef:...<br/>Validity: 2026-03-22 ~ 2027-03-22"] -->|"SHA-256<br/>hash"| B["hash 값<br/>e3b0c44298fc1c14..."] B -->|"ca.key 로<br/>RSA 암호화"| C["Signature Value<br/>3a:cb:99:7f:2e:1a:..."] end subgraph "server.crt 최종 구조" D["평문 데이터<br/>Issuer · Subject · Public Key · Validity"] E["Signature Algorithm: sha256WithRSA<br/>Signature Value: 3a:cb:99:7f:2e:1a:..."] end
flowchart LR subgraph "클라이언트가 검증할 때 (ca.crt 사용)" F["server.crt 평문 데이터<br/>Subject: CN=example.com<br/>Public Key: 00:ab:cd:ef:..."] -->|"SHA-256<br/>hash"| G["hash 값 A<br/>e3b0c44298fc1c14...<br/>(직접 계산)"] H["Signature Value<br/>3a:cb:99:7f:2e:1a:..."] -->|"ca.crt 의 공개키로<br/>RSA 복호화"| I["hash 값 B<br/>(서명에서 추출)"] G --- J{"A == B ?"} I --- J J -->|"같다"| K["✅ CA 가 발급한 진짜 인증서<br/>→ server.crt 안의 공개키를 신뢰"] J -->|"다르다"| L["❌ 위조 또는 변조됨<br/>→ TLS 연결 거부"] end
ca.key로 암호화한 것은ca.crt로만 복호화할 수 있다. (비대칭 암호화)ca.crt로 복호화 성공 → “ca.key소유자(=CA) 만 만들 수 있는 서명이다” → 신뢰한다.
| 단계 | 누가 | 뭘 하는지 | 사용하는 키 |
|---|---|---|---|
| 서명 | CA | 평문 데이터의 SHA-256 hash 를 ca.key 로 RSA 암호화 → Signature Value | ca.key (개인키) |
| 검증 | 클라이언트 | Signature Value 를 ca.crt 로 복호화한 hash 와 직접 계산한 hash 비교 | ca.crt (공개키) |
TLS Handshake 상세
sequenceDiagram participant C as 클라이언트 participant S as 서버 Note over C, S: TCP 3-Way Handshake C->>S: SYN S->>C: SYN-ACK C->>S: ACK Note over C, S: TLS Handshake C->>S: Client Hello<br/>(TLS 버전, 지원 암호화 목록, Client Random) S->>C: Server Hello<br/>(선택된 암호화 방식, Server Random) S->>C: Certificate (server.crt 전달) S->>C: Server Hello Done Note over C: ca.crt 로 server.crt 의 서명 검증<br/>→ server.crt 에서 서버 공개키 추출<br/>(OCSP/CRL 로 인증서 폐지 여부 확인 가능) C->>S: Client Key Exchange<br/>(Pre-Master Secret 을 server.crt 의 공개키로 암호화) Note over C, S: 양측이 동일한 세션 키 생성<br/>(Client Random + Server Random + Pre-Master Secret) C->>S: Change Cipher Spec C->>S: Finished (세션 키로 암호화) S->>C: Change Cipher Spec S->>C: Finished (세션 키로 암호화) Note over S: server.key 로 Pre-Master Secret 복호화<br/>→ 세션 키 생성 완료 Note over C, S: 암호화된 HTTP 통신 (세션 키 사용) C->>S: HTTP Request S->>C: HTTP Response
인증서 폐지 확인 (OCSP / CRL)
- 인증서 서명 검증 자체는 로컬에 저장된 CA 공개키로 수행한다. (CA 와 실시간 통신 불필요)
- 하지만 인증서가 폐지(revoke) 되었는지 확인할 때는 CA 와 통신이 발생할 수 있다.
- CRL (Certificate Revocation List) — CA 가 발행한 폐지 목록을 주기적으로 다운로드하여 확인
- OCSP (Online Certificate Status Protocol) — CA 의 OCSP Responder 에 실시간으로 인증서 상태 질의
- OCSP Stapling 을 사용하면 서버가 미리 OCSP 응답을 받아두고 TLS 핸드셰이크 시 클라이언트에게 함께 전달하므로, 클라이언트가 CA 와 직접 통신하지 않아도 된다.
Kubernetes 에서의 TLS 적용
- Kubernetes 에서 TLS 는 주로 Ingress 에서 TLS Termination 방식으로 적용된다.
- 인증서와 개인키를
kubernetes.io/tls타입의 Secret 으로 저장하고, Ingress 리소스에서 이를 참조한다. - 이렇게 하면 외부 → Ingress 구간은 HTTPS 로 암호화되고, Ingress → Pod 내부 구간은 HTTP 로 통신한다.
sequenceDiagram participant U as 외부 클라이언트 participant I as Ingress Controller participant S as Service participant P as Pod Note over U, I: HTTPS (TLS 암호화) U->>I: HTTPS Request Note over I: TLS Termination<br/>(Secret 에 저장된 인증서/개인키로 복호화) Note over I, P: HTTP (평문 통신) I->>S: HTTP Request S->>P: HTTP Request P->>S: HTTP Response S->>I: HTTP Response I->>U: HTTPS Response
- TLS Secret 생성
kubectl create secret tls my-tls-secret \
--cert=tls.crt \
--key=tls.key- Ingress 에서 TLS 설정
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: my-ingress
spec:
tls:
- hosts:
- example.com
secretName: my-tls-secret
rules:
- host: example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: my-service
port:
number: 80-
cert-manager 를 사용한 인증서 자동 관리
- cert-manager 를 설치하면 Let’s Encrypt 등의 CA 로부터 인증서를 자동 발급/갱신할 수 있다.
ClusterIssuer또는Issuer리소스로 CA 설정 후 Ingress 에 annotation 을 추가하면 TLS Secret 이 자동 생성된다.
-
Service Mesh (e.g. Istio) 를 통한 mTLS
- Ingress → Pod 구간도 암호화가 필요한 경우 Istio 같은 서비스 메시를 사용한다.
- sidecar proxy (Envoy) 가 Pod 간 통신을 자동으로 mTLS 로 암호화한다.
- 애플리케이션 코드 변경 없이 인프라 레벨에서 전 구간 암호화가 가능하다.
Kubernetes 인증서 종류 비교
- Kubernetes 에서 사용되는 인증서 파일은 모두 동일한 PEM 형식이지만, 용도와 서명 주체가 다르다.
| 파일 | 용도 | 서명 주체 |
|---|---|---|
ca.crt | K8s 클러스터 자체 Root CA 인증서 | 자체 서명 (Self-signed) |
etcd-server.crt / .key | etcd 서버 TLS 인증서/키 (컴포넌트 간 내부 통신) | 클러스터 CA (ca.crt) |
apiserver.crt / .key | API Server TLS 인증서/키 (컴포넌트 간 내부 통신) | 클러스터 CA (ca.crt) |
tls.crt / tls.key | Ingress 등 외부 HTTPS 통신용 (Secret 필드명) | 공인 CA (Let’s Encrypt 등) |
-
클러스터 내부 인증서 (
ca.crt,etcd-server.crt,apiserver.crt등)kubeadm init시 자동 생성되며/etc/kubernetes/pki/에 저장된다.- 클러스터의 자체 CA 가 서명하며, API Server ↔ etcd, API Server ↔ kubelet 등 내부 컴포넌트 간 mTLS 통신에 사용된다.
ca.crt는 이 모든 내부 인증서를 서명한 Root CA 인증서이다.
-
외부 통신 인증서 (
tls.crt,tls.key)kubernetes.io/tls타입 Secret 에서 사용하는 필드명 컨벤션이다.- 주로 Ingress 에서 외부 클라이언트와의 HTTPS 통신에 사용한다.
- 보통 공인 CA (Let’s Encrypt 등) 가 서명한 인증서를 사용한다.
flowchart TD subgraph 외부 통신 EXT_CA[공인 CA<br/>Let's Encrypt 등] -->|서명| TLS[tls.crt / tls.key<br/>Ingress TLS Secret] TLS --> ING[Ingress Controller] end subgraph 클러스터 내부 통신 CA[ca.crt<br/>클러스터 자체 CA] -->|서명| API[apiserver.crt / .key<br/>API Server] CA -->|서명| ETCD[etcd-server.crt / .key<br/>etcd] CA -->|서명| KUB[kubelet.crt / .key<br/>kubelet] API <-->|mTLS| ETCD API <-->|mTLS| KUB end
HTTP/1.0, HTTP/1.1, HTTP/2.0
- HTTP/1.0
- keep-alive
- HTTP/1.1
- persistent connection
- pipeline
- HTTP/2.0
HTTP 메시지
HTTP 메시지 구조
start-line
header
empty-line (CRLF)
message-body
HTTP 메시지 예시
# HTTP Request 예시
GET /search?q=hello&hl=ko HTTP/1.1
Host: www.google.com
# HTTP Response 예시
HTTP/1.1 200 OK
Content-Type: text/html;charset=UTF-8
Content-Length: 3423
<html>
<body>...</body>
</html>
- start-line
- request-line (요청 메시지)
methodrequest-targetHTTP-version순으로 구성- e.g.
GET/search?q=hello&hl=koHTTP/1.1
- status-line (응답 메시지)
HTTP-versionstatus-codereason-phrase순으로 구성- e.g.
HTTP/1.1200OK
- request-line (요청 메시지)
- header
- 요청 메시지
- e.g. Host: www.google.com
- 응답 메시지
- e.g. Content-Type: text/html;charset=UTF-8
- 요청 메시지
- message-body
- 실제 전송할 데이터
HTTP 메서드
GET
- 리소스 조회에 주로 사용된다.
- 일반적으로 Request Body 를 입력하지 않으며 레거시 시스템의 경우 요청을 받아들이지 않을 수 있다.
- 서버에 전달하고자 하는 데이터는 query string 을 통해 전달한다.
- 캐싱을 수행하기 때문에 캐싱되지 않는 요청은 GET 요청이 맞지 않을 수 있다.
POST
- 요청 데이터 처리에 주로 사용된다.
- 과거 HTTP 통신은 POST 요청으로 데이터 수정, 삭제도 모두 수행했다.
- POST 요청은 상태를 변화시키기 때문에 멱등성이 보장되지 않는다.
- 보통 Request Body 에 요청 데이터를 담아 전송하고 서버는 해당 데이터를 처리한다.
PUT
- 리소스 덮어쓰기(수정 or 존재하지 않으면 생성)에 주로 사용된다.
- 클라이언트가 리소스의 위치를 알고 있어야 한다.
- 리소스의 위치를 안다는 점에서 POST 와 차이가 있다.
PATCH
- 서버에 존재하는 데이터의 일부를 변경할 때 주로 사용된다.
DELETE
- 리소스 제거에 주로 사용된다.
HTTP 메서드의 속성
안전
- 호출해도 리소스를 변경하지 않는다.
- e.g. GET
멱등
- 한 번이던 100번이던 호출한 결과가 항상 같다.
- e.g GET, PUT, DELETE
캐시가능
- 응답 결과 리소스를 캐시해서 사용해도 되는가?
- e.g. 실무에서는 거의 GET 만 사용한다.
HTTP Request 로 클라이언트에서 서버로 데이터 전송
데이터 전달 방식은 크게 2가지
- GET - query parameter 를 통한 데이터 전송
- POST, PUT, PATCH - message body 를 통한 데이터 전송
4가지 데이터 전송 상황
- GET - 정적 데이터 조회
- 리소스 경로로 단순하게 조회 가능
- GET - 동적 데이터 조회
- query parameter 사용하여 데이터 전달
- HTML Form 데이터 전송
- Content-Type: application/x-www-form-urlencoded
- GET - start-line 에 포함하여 조회

- POST - message body 에 포함하여 저장

- HTTP API 데이터 전송
- HTML Form 전송 대신 JS 를 통한 통신에 사용
- Content-Type: application/json
- GET - query parameter 로 데이터 전달
- POST, PUT, PATCH - message body 를 통해 데이터 전송
HTTP API 설계
HTTP API - 컬렉션
- POST 기반 등록
- 서버가 리소스 URI 결정한다.
HTTP API - 스토어
- PUT 기반 등록
- 클라이언트가 리소스 URI 결정한다.
- 실무에서 거의 사용하지 않는다.
HTML Form 사용
- GET, POST 만 지원
URI 설계 개념
문서 (document)
- 단일 개념 (파일 하나, DB row)
- e.g.
/members/100/files/star.jpg
- e.g.
컬렉션 (collection)
- 서버가 리소스의 URI 를 생성 및 관리
- e.g.
/members
- e.g.
스토어 (store)
- 클라이언트가 리소스의 URI 를 생성 및 관리
- e.g.
/files
- e.g.
컨트롤러 (controller), 컨트롤 URI
- 위 세가지로 처리하기 어려운 프로세스 실행, 동사를 직접 사용
- e.g.
/members/{id}/delete
- e.g.
HTTP 상태 코드
클라이언트가 보낸 요청의 처리 상태를 응답에서 알려주는 기능
1xx (Informational)
- 요청이 수신되어 처리중
- 거의 사용하지 않으므로 생략
2xx (Successful)
- 클라이언트의 요청을 정상적으로 처리
- 200 OK
- 요청 성공
- 201 Created
- 요청 성공해서 새로운 리소스가 생성됨
- 생성된 리소스의 응답은 Location 헤더 필드로 식별할 수 있다.
- 202 Accepted
- 요청이 접수 되었으나 처리가 완료되지 않았음
- 배치 처리 같은 곳에서 사용
- e.g. 요청 접수 후 1시간 뒤에 배치 프로세스가 요청을 처리함
- 사실 잘 사용하지 않음
- 204 No Content
- 서버가 요청을 성공적으로 수행했지만, 응답 페이로드 본문에 보낼 데이터가 없음
- save 버튼의 결과로 아무 내용이 없어도 된다.
- save 버튼을 눌러도 같은 화면을 유지해야 한다.
- 결과 내용이 없어도 204 메시지만으로 성공을 인식할 수 있다.
- e.g. 웹 문서 편집기에서 save 버튼
- 서버가 요청을 성공적으로 수행했지만, 응답 페이로드 본문에 보낼 데이터가 없음
3xx (Redirection)
- 요청을 완료하기 위해 유저 에이전트의 추가 조치 필요
- 웹 브라우저는 3xx 응답의 결과에 Location 헤더가 있으면, Location 위치로 자동 이동 (리다이렉트)
- 영구 리다이렉션 - 301, 308
- 리소스의 URI 가 영구적으로 이동
- 원래 URL 을 사용하지 않음, 검색 엔진 등에서도 변경 인지
- e.g. /members → /users
- e.g. /event → /new-event
- 일시 리다이렉션 - 302, 303, 307
- 리소스의 URI 가 일시적으로 변경
- 따라서 검색 엔진 등에서 URL 을 변경하면 안됨
- e.g. 주문 완료 후 주문 내역 화면으로 이동
- PRG: POST/Redirect/Get
- 특수 리다이렉션 - 300, 304
- 결과 대신 캐시를 사용
- e.g. 요청으로 캐시 사용 가능 여부 전달 → 캐시 사용 가능 시 캐시 사용하도록 응답
- 301 Moved Permanently (영구 리다이렉션)
- 리다이렉트 시 요청 메서드가 GET 으로 변하고, 본문이 제거될 수 있음
- 308 Permanent Redirect (영구 리다이렉션)
- 리다이렉트 시 요청 메서드와 본문을 유지
- 302 Found (일시 리다이렉션)
- 리다이렉트 시 요청 메서드가 GET 으로 변하고, 본문이 제거될 수 있음
- 303 See Other (일시 리다이렉션)
- 리다이렉트 시 요청 메서드가 GET 으로 변경
- 307 Temporary Redirect (일시 리다이렉션)
- 리다이렉트 시 요청 메서드와 본문을 유지
- PRG: POST/Redirect/GET
- 문제: POST 로 주문 후 웹 브라우저를 새로고침하면?
- 새로고침은 다시 요청이기 때문에 중복 주문이 될 수 있음
- 해결: POST 로 주문 후 주문 결과 화면을 GET 메서드로 리다이렉트
- 새로고침 해도 GET 으로 결과 화면만 조회
- 문제: POST 로 주문 후 웹 브라우저를 새로고침하면?
- 304 Not Modified (특수 리다이렉션)
- 캐시를 목적으로 사용
- 클라이언트에게 리소스가 수정되지 않았음을 알려준다.
- 따라서 클라이언트는 로컬에 저장된 캐시를 재사용한다. (캐시로 리다이렉트)
- 304 응답은 로컬 캐시를 사용해야 하므로 응답에 메시지 바디를 포함하면 안된다.
- 캐시를 목적으로 사용
4xx (Client Error)
- 클라이언트 오류, 잘못된 문법등으로 서버가 요청을 수행할 수 없음
- 400 Bad Request
- 클라이언트가 잘못된 요청을 해서 서버가 요청을 처리할 수 없음
- 요청 구문, 메시지 등등 오류
- 클라이언트는 요청 내용을 검토하고 다시 보내야 함
- e.g. 요청 파라미터가 잘못되거나, API 스펙이 맞지 않을 때
- 401 Unauthorized
- 클라이언트가 해당 리소스에 대한 인증이 필요함
- 인증되지 않음
- 401 오류 발생 시 응답에 WWW-Authenticate 헤더와 함께 인증 방법을 설명
- 403 Forbidden
- 서버가 요청을 이해했지만 승인을 거부함
- 주로 인증 자격 증명은 있지만, 접근 권한이 불충분한 경우
- e.g. 어드민 등급이 아닌 사용자가 로그인을 했지만, 어드민 등급의 리소스에 접근하는 경우
- 404 Not Found
- 요청 리소스를 찾을 수 없음
- 요청 리소스가 서버에 없음
- 또는 클라이언트가 권한이 부족한 리소스에 접근할 때 해당 리소스를 숨기고 싶을 때
5xx (Server Error)
- 서버 오류, 서버가 정상 요청을 처리하지 못함
- 서버에 문제가 있기 때문에 재시도 하면 성공할 수도 있음
- 500 Internal Server Error
- 서버 문제로 오류 발생, 애매하면 500 오류
- 503 Service Unavailable
- 서비스 이용 불가
- 서버가 일시적인 과부화 또는 예정된 작업으로 잠시 요청을 처리할 수 없음
- Retry-After 헤더 필드로 얼마뒤에 복구되는지 보낼 수도 있음