실전 HTTP
배포·동시성·정적 콘텐츠·캐싱·쿠키와 세션·HTTPS·HTTP 메서드·HTTP/2까지, 실제 서비스에서 HTTP가 쓰이는 방식을 프론트엔드 관점에서 정리합니다.
앞의 두 레슨에서는 localhost 위에서 HTTP를 다뤘습니다. 이번에는 실제 서비스로 넘어가, 배포·동시성·정적 콘텐츠·캐싱·쿠키와 세션·HTTPS·다양한 메서드·HTTP/2를 정리합니다. 프론트엔드 개발에서 자주 마주치는 주제들입니다.
배포: 서버를 인터넷에 올리기
localhost 서버는 나만 접근할 수 있습니다. 다른 사람이 쓰게 하려면 공개된 곳에 배포해야 합니다. 집에서 직접 호스팅할 수도 있지만 보통은 다음 이유로 호스팅 서비스를 씁니다.
- 클라이언트가 항상 찾아올 수 있도록 고정 IP가 필요한데, 일반 가정용 회선은 IP가 바뀝니다.
- 가정용 공유기는 기본적으로 외부 연결을 차단합니다.
- 서버는 항상 켜져 있어야 합니다.
배포 시 한 가지 중요한 점은 포트를 환경 변수로 받는 것입니다. 호스팅 환경은 여러 앱을 한 컴퓨터에서 돌리므로, 어느 포트를 쓸지 플랫폼이 지정합니다.
const port = process.env.PORT || 8000; // 환경 변수가 있으면 그 값을, 없으면 8000
server.listen(port);오늘날의 프론트엔드 배포(Vercel, Netlify, Cloudflare 등)는 이 과정을 추상화해 Git push만으로 처리하지만, 그 아래에서는 같은 원리가 동작합니다.
동시성: 여러 요청을 동시에 처리하기
한 페이지를 그리려면 HTML·CSS·이미지·API 응답을 받아야 하고, 브라우저는 이들을 병렬로 요청합니다. 서버가 한 번에 요청 하나만 처리하면 나머지는 기다려야 하므로, 서버는 여러 요청을 동시에 다뤄야 합니다. 응답이 클라이언트에 도착하기 전까지 “떠 있는” 요청을 in-flight 요청이라고 합니다.
요청 처리에는 시간이 걸립니다(요청 파싱, 데이터 생성, 전송, 네트워크 지연). 초당 많은 요청을 받는 서비스라면 항상 다수의 요청이 동시에 진행 중입니다.
초당 3,000개의 페이지 뷰가 있고, 한 페이지 뷰가 3개의 HTTP 요청을 만들며, 각 요청이 0.1초 걸린다면 평균적으로 동시에 진행 중인 요청 수는?
정적 콘텐츠와 리버스 프록시
웹의 많은 데이터는 잘 바뀌지 않는 정적 콘텐츠입니다 — 이미지, CSS, JS 번들, 폰트, 동영상 등. Nginx, Apache 같은 전용 웹 서버는 이런 파일을 디스크에서 매우 빠르게 내보냅니다.
여러 서버 구성요소를 둔 서비스에서는 앞단의 서버가 요청을 적절한 백엔드로 분배합니다.
- 라우팅 / 리버스 프록시 — 요청을 처리할 백엔드로 전달.
- 로드 밸런싱 — 부하를 여러 서버에 분산. 한 서버가 죽어도 다른 서버가 받아주고, 무중단 배포도 가능.
- 헬스 체크 — 정상인 백엔드에만 요청을 보냄.
프론트엔드 관점에서는, 정적 자산은 빠른 정적 서버(또는 CDN)가, 동적 데이터는 애플리케이션 서버가 담당하도록 나누는 구조를 이해해 두면 좋습니다.
캐싱
같은 리소스를 매번 다시 만들거나 다시 보내는 것은 낭비입니다. 캐시는 재사용될 가능성이 높은 리소스를 임시 저장합니다. 캐싱은 여러 지점에서 일어납니다.
- 브라우저 캐시 — 각 사용자의 브라우저가 최근 받은 자산을 저장.
- 프록시 캐시 / CDN — 여러 사용자를 대신해 캐싱.
- 리버스 프록시 캐시 — 느린 애플리케이션 서버·DB 대신 결과를 캐싱.
캐싱은 모두 서버가 보내는 캐시 제어 헤더로 통제됩니다. Cache-Control, ETag, Last-Modified 등으로 “이 리소스는 자주 안 바뀌니 캐시해도 된다”를 알립니다.
정적 자산을 캐시하면 사용자 경험이 빨라지고, 서버가 처리할 요청 수도 줄어 더 많은 트래픽을 감당할 수 있습니다.
초당 6,000개의 요청 중 1/3이 잘 안 바뀌는 CSS 요청이다. 캐시를 적용해 방문자의 1%만 CSS를 새로 받게 하면 전체 요청 수는 대략 얼마가 되는가?
쿠키와 세션
HTTP는 기본적으로 상태가 없습니다(stateless). 각 요청은 독립적이라 서버는 “이 요청이 누구인지” 알 수 없습니다. 쿠키는 이 문제를 해결합니다. 서버가 브라우저에 작은 데이터를 저장하게 하고, 이후 요청마다 그 데이터를 돌려받습니다.
# 서버 → 브라우저 (응답)
Set-Cookie: session=abc123; Max-Age=600; HttpOnly; Secure
# 브라우저 → 서버 (이후 모든 요청)
Cookie: session=abc123쿠키는 로그인 상태 유지, 세션 관리, 사용자 추적, 환경설정 저장 등에 쓰입니다. 주요 속성은 다음과 같습니다.
Domain/Path— 쿠키를 어떤 요청에 함께 보낼지 범위를 정함.Secure— HTTPS 연결에서만 전송.HttpOnly— JavaScript에서 접근 불가. XSS로 쿠키가 탈취되는 것을 막음.Expires/Max-Age— 쿠키의 수명. 없으면 브라우저를 닫을 때 만료.
보안을 위해, 쿠키에는 사용자 정보를 직접 담기보다 세션 ID만 담고 실제 정보는 서버 측 저장소에 두는 방식을 흔히 씁니다. 사용자가 쿠키 값을 위조할 수 있으므로, Express·Rails 같은 프레임워크는 쿠키에 서명을 붙여 변조를 탐지합니다.
쿠키 데이터를 HTML로 표시할 때는 특수문자를 이스케이프해야 합니다. 그렇지 않으면 XSS 취약점이 생길 수 있습니다.
DNS와 쿠키 보안
쿠키는 도메인에 묶입니다. 만약 공격자가 브라우저에게 자신의 서버가 facebook.com의 일부인 것처럼 믿게 만들면, 브라우저는 facebook.com 쿠키를 공격자에게 보냅니다. 그 쿠키로 계정에 접근할 수 있으므로, DNS와 도메인의 신뢰는 웹 보안의 핵심입니다.
HTTPS와 TLS
HTTPS는 HTTP를 암호화된 연결 위에서 주고받는 것입니다. 암호화는 TLS(Transport Layer Security) 프로토콜을 따릅니다(예전 이름 SSL). TLS는 세 가지를 보장합니다.
- 기밀성(privacy) — 연결 내용을 암호화해 중간에서 엿볼 수 없게 함.
- 인증(authentication) — 접속한 서버가 진짜 그 서버임을 확인.
- 무결성(integrity) — 전송 중 데이터가 변조되지 않았음을 검증.
서버는 개인 키(비공개)와 인증서(브라우저로 전송)를 갖습니다. 인증서는 인증 기관(CA) 이 발급하며, 그 서버가 해당 도메인의 실제 소유자임을 보증합니다. 인증서에는 유효한 도메인 정보가 담겨 있어, 도메인이 맞지 않으면 브라우저가 경고를 띄웁니다.
오늘날 대부분의 사이트는 모든 연결에 HTTPS를 기본으로 씁니다. 프론트엔드에서는 Secure 쿠키, 혼합 콘텐츠(HTTPS 페이지에서 HTTP 자원 로드) 차단, 일부 브라우저 API가 HTTPS에서만 동작하는 점 등을 마주치게 됩니다.
카페 와이파이에서 HTTPS로 로그인할 때, TLS가 막아주는 위협이 아닌 것은?
GET·POST 외의 메서드
브라우저가 기본으로 만드는 요청은 GET(링크 이동)과 POST(폼 전송)뿐이지만, API에서는 다른 메서드도 씁니다. 이들은 JavaScript 코드에서 fetch로 직접 지정해야 합니다.
| 메서드 | 용도 |
|---|---|
| GET | 리소스 조회. 멱등. |
| POST | 리소스 생성·동작 실행. 멱등하지 않음. |
| PUT | 리소스를 지정한 위치에 생성·교체. 성공 시 201 Created. 멱등. |
| DELETE | 리소스 삭제. 이후 GET은 404. |
| PATCH | 리소스를 부분적으로 수정. JSON Patch 등 형식은 앱이 정함. |
| HEAD | GET과 같지만 본문 없이 헤더만 받음. |
| OPTIONS | 서버가 지원하는 기능 확인. CORS preflight에서 사용. |
await fetch('/api/users/42', { method: 'DELETE' });
await fetch('/api/users/42', {
method: 'PATCH',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ name: '새 이름' }),
});메서드의 의미를 지키는 것은 중요합니다. GET이 데이터를 변경하도록 만들면, 링크를 자동으로 따라가는 검색 크롤러가 의도치 않게 데이터를 지우는 사고가 날 수 있습니다(실제 사례가 있습니다). GET은 부작용이 없어야 합니다.
프론트엔드에서 자주 만나는
OPTIONS는 CORS preflight 요청입니다. 다른 출처(origin)로 PUT·DELETE·커스텀 헤더가 포함된 요청을 보내기 전에, 브라우저가 먼저OPTIONS로 “이 요청을 보내도 되는지” 서버에 묻습니다.
HTTP/2
HTTP는 오랜 시간 발전해 왔습니다. 0.9는 GET만, 1.0은 헤더·POST·상태 코드·Content-Type을, 1.1은 캐싱 개선과 Host 헤더(한 서버에 여러 사이트)를 추가했습니다. HTTP/2는 많은 요청을 다루는 서비스의 효율을 높이는 데 초점을 둡니다.
HTTP/1.1에는 한계가 있습니다. 브라우저는 한 서버에 보통 최대 6개의 연결만 열고, 각 연결은 한 번에 요청 하나만 처리합니다. 작은 파일을 수백 개 받아야 하면 6개씩 끊어 받느라 느려지고, 지연(latency)이 클수록 더 심해집니다.
HTTP/2는 이를 다음으로 개선합니다.
- 멀티플렉싱(multiplexing) — 하나의 연결로 여러 요청·응답을 동시에 주고받습니다. 6개 제한이 사라져, 지연이 클 때 특히 빨라집니다.
- 서버 푸시(server push) — 브라우저가 HTML을 요청하면, 곧 필요할 CSS 등을 서버가 미리 함께 보냅니다.
- 헤더 압축 — 반복되는 헤더를 압축해 오버헤드를 줄입니다.
표준에서 암호화가 필수로 명시되지는 않았지만, 주요 브라우저는 TLS 위에서만 HTTP/2를 사용합니다. 사실상 HTTP/2는 HTTPS를 전제로 합니다.
작은 이미지 수백 개를 불러오는 페이지가 HTTP/2에서 더 빠른 주된 이유는?
정리 · 실제 서비스의 HTTP에는 배포·동시성·정적 콘텐츠 분리·캐싱이 얽혀 있고, 쿠키와 세션으로 상태를 유지하며, HTTPS로 연결을 보호합니다. API는 다양한 메서드를 쓰고, HTTP/2는 멀티플렉싱으로 많은 요청을 효율적으로 처리합니다. 이 개념들은 프론트엔드 디버깅·성능 최적화·보안의 바탕이 됩니다.
이 레슨을 다 읽으셨나요?
완료 표시는 이 브라우저에 저장됩니다.