
HTTPS: 자물쇠가 달린 투명 인간
HTTP는 엽서라서 우체부가 내용을 훔쳐볼 수 있습니다. HTTPS는 봉투에 밀봉해서 보내는 기술입니다.

HTTP는 엽서라서 우체부가 내용을 훔쳐볼 수 있습니다. HTTPS는 봉투에 밀봉해서 보내는 기술입니다.
내 서버는 왜 걸핏하면 뻗을까? OS가 한정된 메모리를 쪼개 쓰는 처절한 사투. 단편화(Fragmentation)와의 전쟁.

미로를 탈출하는 두 가지 방법. 넓게 퍼져나갈 것인가(BFS), 한 우물만 팔 것인가(DFS). 최단 경로는 누가 찾을까?

프론트엔드 개발자가 알아야 할 4가지 저장소의 차이점과 보안 이슈(XSS, CSRF), 그리고 언제 무엇을 써야 하는지에 대한 명확한 기준.

이름부터 빠릅니다. 피벗(Pivot)을 기준으로 나누고 또 나누는 분할 정복 알고리즘. 왜 최악엔 느린데도 가장 많이 쓰일까요?

개발 초기, 경험 많은 개발자가 충격적인 말을 했습니다:
"카페 와이파이에서 HTTP 사이트로 로그인하면, 같은 와이파이 쓰는 사람이 너 비밀번호 다 봐."
저: "진짜요?!"
시니어: "Wireshark 프로그램 켜보면 다 보여. 그래서 HTTPS 써야지."
제 첫 반응은 "설마 그럴 리가"였습니다. 로그인할 때 비밀번호 칸에 ***로 가려지는데, 어떻게 남이 볼 수 있다는 건지 믿기지 않았습니다.
그런데 시니어가 직접 Wireshark를 켜고 제가 테스트 서버에 로그인하는 모습을 보여줬습니다. 제 아이디와 비밀번호가 그대로 화면에 떴습니다. 그날 밤 집에 가서 모든 사이트의 주소창을 확인했습니다. 🔒 표시가 없는 사이트는 모두 비밀번호를 바꿨습니다.
제가 처음 프로젝트에서 간단한 내부 툴을 만들었을 때, HTTP로 배포했습니다. 별 문제 없이 잘 돌아갔습니다. 그런데 회사에서 "왜 HTTPS 써야 하나요?"라고 물었더니 세 방향에서 압박이 들어왔습니다:
결국 돈 문제, 법 문제, 보안 문제 전부였습니다. 저는 당장 Let's Encrypt로 인증서를 발급받았습니다.
HTTPS를 공부하면서 머리가 복잡해졌습니다:
무엇보다 "어떻게 암호화하는 거야?"가 제일 이해가 안 갔습니다. 브라우저와 서버가 처음 만날 때 비밀키를 어떻게 안전하게 주고받는지 도무지 와닿지 않았습니다.
시니어가 해준 비유를 듣고 나서야 결국 이거였다는 깨달음이 왔습니다:
"아, 암호화가 봉투구나!"HTTP = 엽서: "카페에서 친구한테 엽서를 보냈어. '은행 비밀번호 1234야' 적었는데, 우체부, 배달원, 커피숍 알바까지 다 봤어. 너 계좌 비었어."
HTTPS = 밀봉 편지 + 철제 금고: "같은 내용을 강철 봉투에 넣고, 자물쇠 10개 채워서 보냈어. 받는 사람만 열쇠 있어. 중간에 누가 뜯으려 해도 안돼."
이 비유를 받아들이고 나니 모든 게 이해되기 시작했습니다. HTTP는 모든 내용이 투명하게 보이는 엽서고, HTTPS는 누구도 열 수 없는 밀봉 편지라는 개념이 머릿속에 확 들어왔습니다.
HTTP는 모든 데이터를 평문(Plain Text)으로 전송합니다. 암호화가 전혀 없습니다.
클라이언트 → 서버
POST /login HTTP/1.1
Host: bank.com
Content-Type: application/x-www-form-urlencoded
username=ratia&password=1234
문제: 이 패킷을 중간에 누가 가로채면? 그대로 보입니다.
제가 직접 테스트로 해본 것입니다:
# 1. 카페 와이파이 접속
# 2. Wireshark 프로그램 실행
# 3. HTTP 패킷 필터링
http.request.method == "POST"
결과:
POST /login HTTP/1.1
Host: test.local
username=ratia
password=1234
그대로 보임! 😱
이 테스트를 하고 나서 공포스러웠습니다. 내가 매일 쓰는 공용 와이파이에서, 누군가 Wireshark만 켜놓으면 내 비밀번호를 다 볼 수 있다는 걸 깨달았습니다.
더 무서운 건 MITM 공격입니다:
클라이언트 → [해커] → 서버
1. 클라이언트: "10만원 송금"
2. 해커: "100만원 송금"으로 변조
3. 서버: "100만원 송금 완료"
HTTP는 데이터 무결성을 보장하지 않아서, 중간에 누가 내용을 바꿔도 모릅니다.
HTTPS는 세 가지를 보장합니다. 이 세 가지를 정리해본다면:
암호화 전:
username=ratia&password=1234
암호화 후:
7x9$mK#@pL2qR5vN8cX#Fg2...
해커가 패킷을 훔쳐도 암호문만 보입니다. 복호화 없이는 원본을 알 수 없습니다.
중간에 누가 데이터를 바꿨는지 감지합니다:
원본: "10만원 송금"
변조: "100만원 송금" ← HMAC으로 감지됨!
HTTPS는 메시지 인증 코드(MAC)를 사용해서 데이터가 변조되었는지 검증합니다.
"이 사이트가 진짜 은행 맞나?"
인증서(Certificate)로 서버의 신원을 확인합니다. 피싱 사이트가 진짜 은행처럼 꾸며도, 인증서가 없으면 브라우저가 경고를 띄웁니다.
처음에 제일 헷갈렸던 게 이 부분입니다. "SSL 인증서"라고 부르는데, 기술 문서엔 "TLS"라고 적혀 있으니까요.
SSL 1.0 (1994) - 출시 전 폐기 (심각한 보안 결함)
SSL 2.0 (1995) - 보안 취약점 발견
SSL 3.0 (1996) - POODLE 공격에 취약
───────────────────────────────
TLS 1.0 (1999) - SSL 3.0 업그레이드, 새 이름
TLS 1.1 (2006) - CBC 공격 방어
TLS 1.2 (2008) ← 현재 가장 많이 사용
TLS 1.3 (2018) ← 최신 표준, 더 빠르고 안전
현실:
왜? 입에 붙어서! SSL이란 이름이 먼저 유명해졌기 때문에 지금도 SSL이라고 부릅니다.
제가 이해했다고 느낀 순간은, "SSL은 옛날 이름이고 지금은 TLS다. 하지만 사람들은 습관적으로 SSL이라 부른다"는 걸 받아들인 때였습니다.
HTTPS가 어떻게 암호화 통신을 시작하는지, 이 과정을 이해하는 게 핵심입니다.
1. Client Hello
클라이언트: "안녕! 나 암호화 하고 싶어. 내가 쓸 수 있는 암호는:
- TLS 1.3
- AES-256-GCM
- RSA-2048
- Random Number: a3f82d..."
2. Server Hello
서버: "OK! TLS 1.3 + AES-256 쓰자.
여기 내 **인증서** 받아.
Random Number: 8e12c9..."
3. 인증서 검증
브라우저: "이 인증서 진짜인지 CA한테 확인..."
CA: "맞아, 내가 DigiCert야. 내 서명 확인해봐."
브라우저: (DigiCert 공개키로 서명 검증)
브라우저: "인증서 진짜네!"
4. 키 교환
클라이언트: (비밀키 생성) → 서버 공개키로 암호화 → 전송
서버: 내 개인키로 복호화 → 비밀키 획득
양쪽 다 같은 비밀키 보유!
5. 암호화 통신 시작
이제부터 모든 데이터는 비밀키로 암호화!
이 과정이 0.1초만에 끝납니다. 우리가 주소창에 https:// 치고 엔터 누르면, 이 복잡한 과정이 눈 깜짝할 사이에 완료됩니다.
HTTPS의 핵심은 하이브리드 암호화입니다. 두 가지 암호화를 섞어 쓴다는 뜻입니다.
같은 키로 암호화/복호화
암호화: plaintext + key123 → ciphertext
복호화: ciphertext + key123 → plaintext
장점: 빠름 ⚡ (AES-256은 1GB 데이터를 1초에 처리) 단점: 키를 어떻게 안전하게 전달? 엽서로 키를 보내면 의미 없음!
공개키로 암호화, 개인키로 복호화
암호화: plaintext + 공개키 → ciphertext
복호화: ciphertext + 개인키 → plaintext
장점: 키 전달 안전 (공개키는 누구한테 줘도 상관없음) 단점: 느림 🐢 (대칭키보다 100배 이상 느림)
최고의 조합! 안전함과 속도를 모두 잡았습니다.
이 구조를 이해했을 때, 제게 와닿았다는 느낌이 왔습니다. "아, 둘 다 써서 장점만 취하는 거구나!"
"이 사이트가 진짜 네이버라는 걸 증명"
만약 인증서가 없다면? 누구나 naver.com을 사칭할 수 있습니다. 피싱 사이트가 "저 네이버예요"라고 주장하면 믿어야 할까요? 인증서가 이걸 막아줍니다.
발급 대상: naver.com
발급자: DigiCert (CA)
유효기간: 2024.01.01 ~ 2025.01.01
공개키: MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA...
서명: (DigiCert의 개인키로 서명)
1. 브라우저: "이 인증서 진짜야?"
2. 브라우저 내장 CA 목록 확인 (Chrome, Firefox에 내장)
3. DigiCert 공개키로 서명 검증
4. 서명 일치 → "진짜 네이버!"
5. 서명 불일치 → 🚨 "경고: 신뢰할 수 없는 사이트"
Root CA (최상위 인증 기관)
└─ Intermediate CA (중간 인증 기관)
└─ example.com (최종 인증서)
브라우저는 Root CA만 신뢰합니다. 나머지는 체인을 따라 올라가면서 검증합니다.
제가 회사 서버에 HTTPS를 적용한 과정입니다:
# 1. Certbot 설치 (Ubuntu/Debian)
sudo apt-get update
sudo apt-get install certbot python3-certbot-nginx
# 2. 인증서 발급 (자동)
sudo certbot --nginx -d example.com -d www.example.com
# Certbot이 자동으로:
# - 도메인 소유권 검증
# - 인증서 발급
# - Nginx 설정 파일 수정
# - HTTPS 리다이렉션 설정
# 3. 자동 갱신 설정 (인증서는 90일마다 만료)
sudo certbot renew --dry-run
# 4. Cron으로 자동 갱신
sudo crontab -e
# 매일 새벽 3시에 갱신 시도
0 3 * * * certbot renew --quiet
결과: 5분 만에 무료로 HTTPS 완성!
예전엔 연간 수십만 원씩 내고 Comodo, DigiCert에서 인증서를 샀는데, 지금은 Let's Encrypt 덕분에 무료입니다. 와닿았다는 게, "인증서 = 비싼 것"이라는 편견이 깨진 순간이었습니다.
TLS 1.2는 핸드셰이크에 2-RTT가 필요했습니다:
TLS 1.2:
Client → Server: Client Hello
Client ← Server: Server Hello
Client → Server: Key Exchange
암호화 통신 시작 (총 2왕복)
TLS 1.3은 1-RTT로 줄였습니다:
TLS 1.3:
Client → Server: Client Hello + Key Share
Client ← Server: Server Hello + Key Share
암호화 통신 시작 (총 1왕복)
0-RTT Resume은 이전에 접속한 적 있는 서버에 재접속할 때, 핸드셰이크 없이 바로 암호화 데이터를 보낼 수 있습니다.
TLS 1.3 Resume:
Client → Server: Application Data (암호화됨)
바로 통신 시작! (0왕복)
속도 향상: 첫 접속 50% 빠름, 재접속 200% 빠름
HTTPS 페이지에서 HTTP 리소스를 로드하면 브라우저가 차단합니다:
<!-- HTTPS 페이지 (https://example.com) -->
<img src="http://cdn.example.com/image.jpg"> ❌ 차단됨!
<script src="http://analytics.com/script.js"></script> ❌ 차단됨!
해결책:
<!-- 프로토콜 상대 URL 사용 -->
<img src="//cdn.example.com/image.jpg"> ✅
<script src="//analytics.com/script.js"></script> ✅
<!-- 또는 HTTPS로 변경 -->
<img src="https://cdn.example.com/image.jpg"> ✅
제가 실제로 겪은 문제입니다. HTTPS로 전환했는데 이미지가 안 보여서 당황했습니다. 개발자 도구 콘솔을 보니 "Mixed Content Blocked" 오류가 떴습니다.
Let's Encrypt 인증서는 90일마다 만료됩니다. 자동 갱신 설정을 안 하면?
2025-05-14: 인증서 발급
2025-08-14: 인증서 만료 ← 사이트 접속 불가!
해결책: Certbot 자동 갱신 + 모니터링
# 갱신 테스트
sudo certbot renew --dry-run
# 만료 30일 전 알림 이메일
sudo certbot renew --email admin@example.com
교훈: 저는 회사 서버가 인증서 만료로 3시간 다운된 적이 있습니다. 새벽에 긴급 출근했습니다. 😭
HSTS는 브라우저에게 "이 사이트는 무조건 HTTPS만 써"라고 강제하는 헤더입니다:
# Nginx 설정
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload" always;
장점:
주의사항:
includeSubDomains: 모든 서브도메인도 HTTPS 필수preload: Chrome HSTS Preload List에 등록하면 영구적으로 HTTPS 강제함정: HSTS 설정 후 HTTP로 되돌리기 매우 어려움. 사용자 브라우저에 이미 캐시되어서 수정 불가.
개발 환경에서는 괜찮지만, 프로덕션에서는 절대 안 됩니다:
# 테스트용 자체 서명 인증서 생성
openssl req -x509 -newkey rsa:2048 -keyout key.pem -out cert.pem -days 365
문제:
해결: 프로덕션에서는 반드시 Let's Encrypt나 상용 CA 인증서 사용
일반 HTTPS는 서버만 인증합니다. 하지만 마이크로서비스 환경에서는 클라이언트도 인증해야 합니다.
일반 TLS:
Client → Server: "당신이 진짜 서버인지 인증서 보여줘"
Server → Client: "여기 내 인증서"
(서버만 인증됨)
mTLS:
Client → Server: "당신이 진짜 서버인지 인증서 보여줘"
Server → Client: "여기 내 인증서. 너도 인증서 보여줘"
Client → Server: "여기 내 인증서"
(양쪽 다 인증됨)
사용 사례:
server {
listen 443 ssl;
# 서버 인증서
ssl_certificate /etc/ssl/server.crt;
ssl_certificate_key /etc/ssl/server.key;
# 클라이언트 인증서 요구
ssl_verify_client on;
ssl_client_certificate /etc/ssl/ca.crt;
}
문제: CA가 잘못된 인증서를 발급하면?
2011년, DigiNotar CA가 해킹당해 google.com 위조 인증서가 발급되었습니다. 이란 정부가 Gmail 접속을 감시하는 데 사용했습니다.
해결책: Certificate Transparency
1. CA가 인증서 발급
2. CT Log 서버에 기록 (공개)
3. 누구나 조회 가능
4. 이상한 인증서 발견 → 신고
확인 방법:
# crt.sh에서 도메인별 인증서 내역 조회
curl "https://crt.sh/?q=example.com&output=json"
제 도메인을 조회했더니, 제가 발급받은 Let's Encrypt 인증서가 그대로 기록되어 있었습니다. 투명성이 이렇게 구현되는구나 싶었습니다.
개발할 때도 HTTPS 테스트가 필요한 이유:
# 1. mkcert 설치
brew install mkcert
# 또는
curl -JLO "https://dl.filippo.io/mkcert/latest?for=darwin-amd64"
chmod +x mkcert-v*-darwin-amd64
sudo mv mkcert-v*-darwin-amd64 /usr/local/bin/mkcert
# 2. 로컬 CA 설치
mkcert -install
# 3. localhost 인증서 생성
mkcert localhost 127.0.0.1 ::1
# 생성된 파일:
# localhost+2.pem (인증서)
# localhost+2-key.pem (개인키)
const https = require('https');
const fs = require('fs');
const express = require('express');
const app = express();
const options = {
key: fs.readFileSync('./localhost+2-key.pem'),
cert: fs.readFileSync('./localhost+2.pem')
};
https.createServer(options, app).listen(443, () => {
console.log('HTTPS 서버 실행: https://localhost');
});
이제 https://localhost 가능!
HTTPS는 HTTP/2의 필수 조건입니다:
HTTP/1.1 + HTTP: 가능 ✅
HTTP/1.1 + HTTPS: 가능 ✅
HTTP/2 + HTTP: 불가능 ❌
HTTP/2 + HTTPS: 가능 ✅
HTTP/2 장점:
속도 비교:
HTTP/1.1 + HTTPS: 100개 이미지 로드 → 5초
HTTP/2 + HTTPS: 100개 이미지 로드 → 2초 (60% 빠름!)
결론: HTTPS 쓰면 보안뿐 아니라 속도도 빨라집니다!
⚠️ 주의 요함
https가 아닙니다
이 사이트에 입력하는 정보(비밀번호, 신용카드 등)는 안전하지 않습니다
영향:
🔒 연결이 안전함
인증서가 유효합니다 (DigiCert)
효과: 사용자 신뢰도 ↑
제가 실제로 HTTPS를 적용할 때 사용하는 체크리스트입니다:
| 항목 | 설명 |
|---|---|
| 암호화 | 대칭키 (빠름) + 비대칭키 (안전) |
| 인증서 | Let's Encrypt (무료) or DigiCert (유료) |
| 프로토콜 | TLS 1.2 이상 (TLS 1.3 권장) |
| 포트 | 443 (HTTP는 80) |
| 자동 갱신 | Certbot cron 설정 (90일마다) |
| HSTS | max-age=31536000 설정 |
| Mixed Content | 모든 리소스 HTTPS로 변경 |
| 리다이렉트 | HTTP → HTTPS 301 리다이렉트 |
| 모니터링 | 인증서 만료 30일 전 알림 |
| SEO | Google Search Console에 HTTPS 버전 등록 |
| 비용 | Let's Encrypt 무료 |
처음엔 "HTTPS가 뭐가 중요해? HTTP도 잘 되는데"라고 생각했습니다.
지금은 습관적으로 주소창을 봅니다:
제가 배운 교훈을 정리해본다면:
결국 이거였다: HTTPS는 선택이 아니라 필수입니다. 보안, 법률, SEO, 속도 모든 면에서 이점이 있습니다.
여러분의 비밀번호는 밀봉 편지로 보내세요. 🔒