8장: HTTP 통신 이해

📋 강의 개요


📚 8.1 HTTP 프로토콜이란?

강의 포인트

HTTP란?

HTTP (HyperText Transfer Protocol)
= 웹에서 데이터를 주고받기 위한 프로토콜(규약)

클라이언트 (브라우저) ↔ HTTP ↔ 서버

HTTP의 특징

1. Stateless (무상태성)

서버는 클라이언트의 상태를 저장하지 않음

요청 1: 로그인 → 응답: 성공
요청 2: 데이터 조회 → 서버는 로그인 상태를 모름!

해결: Cookie, Session, Token

2. Connectionless (비연결성)

요청 → 응답 → 연결 종료

장점: 서버 자원 효율적 사용
단점: 매번 연결/종료 오버헤드

해결: Keep-Alive (HTTP/1.1부터 기본)

3. 클라이언트-서버 구조

클라이언트: 요청 (Request)
서버: 응답 (Response)

역할 분리 → 독립적 개발 가능

📚 8.2 HTTP 버전

강의 포인트

HTTP/1.0 (1996년)

특징:
- 연결 → 요청 → 응답 → 종료 (매번)
- 하나의 연결에 하나의 요청

문제:
- 느림 (연결 비용 높음)

HTTP/1.1 (1997년) ⭐ 현재 가장 널리 사용

개선:
✅ Keep-Alive (연결 재사용)
✅ Pipelining (요청 여러 개 동시 전송)
✅ Host 헤더 (가상 호스팅 지원)
✅ 캐시 제어 강화

문제:
❌ Head-of-Line Blocking (앞 요청이 느리면 뒤도 대기)

HTTP/2 (2015년)

개선:
✅ Multiplexing (하나의 연결로 여러 요청/응답 동시 처리)
✅ Header 압축
✅ Server Push (서버가 먼저 전송)
✅ Binary Protocol (텍스트 → 바이너리)

성능: 30-50% 향상

HTTP/3 (2022년)

개선:
✅ QUIC 프로토콜 사용 (UDP 기반)
✅ 더 빠른 연결 수립
✅ 패킷 손실에 강함

아직 도입 초기 단계

📚 8.3-8.4 HTTP 메시지 구조

강의 포인트

HTTP 요청 (Request) 구조

GET /users/123 HTTP/1.1              ← 요청 라인 (Request Line)
Host: api.example.com                ← 헤더 (Headers)
Content-Type: application/json
Authorization: Bearer token123
                                      ← 빈 줄
{"name": "홍길동"}                    ← 본문 (Body, 선택)

요청 라인 (Request Line)

메서드  경로  HTTP버전
  ↓     ↓     ↓
GET /users HTTP/1.1

메서드: GET, POST, PUT, DELETE 등
경로: 요청 대상 리소스
HTTP버전: HTTP/1.1, HTTP/2 등

HTTP 응답 (Response) 구조

HTTP/1.1 200 OK                      ← 상태 라인 (Status Line)
Content-Type: application/json       ← 헤더 (Headers)
Content-Length: 50
Set-Cookie: session=abc123
                                      ← 빈 줄
{"id": 123, "name": "홍길동"}         ← 본문 (Body)

상태 라인 (Status Line)

HTTP버전  상태코드  상태메시지
   ↓       ↓        ↓
HTTP/1.1 200     OK

상태코드: 200, 404, 500 등
상태메시지: OK, Not Found, Internal Server Error 등

📚 8.5 HTTP 메서드

강의 포인트

GET - 조회 (Read)

// 데이터 조회 (쿼리 파라미터 사용)
fetch('https://api.example.com/users?page=1&size=10')

특징:
 안전 (Safe): 서버 상태를 변경하지 않음
 멱등성 (Idempotent): 여러  실행해도 같은 결과
 캐시 가능
 Body 사용 불가 (URL에 데이터 포함)

예시:

GET /users              → 사용자 목록 조회
GET /users/123          → 사용자 123 조회
GET /users?name=홍길동   → 이름으로 검색

POST - 생성 (Create)

// 새 데이터 생성
fetch('https://api.example.com/users', {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({
        name: '홍길동',
        email: 'hong@example.com'
    })
})

특징:
 Body에 데이터 전송
 안전하지 않음 (서버 상태 변경)
 멱등성 없음 (실행할 때마다  리소스 생성)
 캐시 불가

예시:

POST /users             → 새 사용자 생성
POST /posts             → 새 게시글 작성
POST /login             → 로그인

PUT - 전체 수정 (Update)

// 리소스 전체를 새 데이터로 교체
fetch('https://api.example.com/users/123', {
    method: 'PUT',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({
        name: '김철수',        // 전체 데이터
        email: 'kim@example.com',
        age: 30
    })
})

특징:
 멱등성 있음 (여러  실행해도 같은 결과)
 안전하지 않음

예시:

PUT /users/123          → 사용자 123 전체 수정
PUT /posts/456          → 게시글 456 전체 수정

PATCH - 부분 수정 (Partial Update)

// 리소스의 일부만 수정
fetch('https://api.example.com/users/123', {
    method: 'PATCH',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({
        age: 31                 // 나이만 수정
    })
})

특징:
 PUT보다 효율적 (변경할 부분만 전송)
 멱등성 보장   (구현에 따라 다름)

PUT vs PATCH:

PUT: 전체 교체
  기존: {name: "홍길동", age: 30, email: "hong@..."}
  요청: {name: "홍길동", age: 31}
  결과: {name: "홍길동", age: 31}  ← email 사라짐!

PATCH: 부분 수정
  기존: {name: "홍길동", age: 30, email: "hong@..."}
  요청: {age: 31}
  결과: {name: "홍길동", age: 31, email: "hong@..."}  ← email 유지

DELETE - 삭제 (Delete)

// 리소스 삭제
fetch('https://api.example.com/users/123', {
    method: 'DELETE'
})

특징:
 멱등성 있음 (여러  삭제해도 결과 같음)
 안전하지 않음

예시:

DELETE /users/123       → 사용자 123 삭제
DELETE /posts/456       → 게시글 456 삭제

OPTIONS - 지원 메서드 확인

// 서버가 지원하는 메서드 확인
fetch('https://api.example.com/users', {
    method: 'OPTIONS'
})

// 응답 헤더
Allow: GET, POST, PUT, DELETE

HEAD - 헤더만 조회

// Body 없이 헤더만 가져옴
fetch('https://api.example.com/users/123', {
    method: 'HEAD'
})

// 사용 예: 리소스 존재 확인, 메타데이터 조회

CRUD 대응표

CRUD      HTTP Method
-------   -----------
Create    POST
Read      GET
Update    PUT / PATCH
Delete    DELETE

📚 8.6 멱등성과 안전성

강의 포인트

안전 (Safe)

서버의 상태를 변경하지 않는가?

✅ 안전: GET, HEAD, OPTIONS
❌ 불안전: POST, PUT, PATCH, DELETE

멱등성 (Idempotent)

여러 번 실행해도 결과가 같은가?

✅ 멱등:
  GET /users/123     → 항상 같은 결과
  PUT /users/123     → 몇 번을 실행해도 같은 상태
  DELETE /users/123  → 이미 삭제되었으면 404 (상태 같음)

❌ 비멱등:
  POST /users        → 실행할 때마다 새 사용자 생성

비교표

메서드 안전 멱등 캐시 가능
GET
POST
PUT
PATCH
DELETE
HEAD
OPTIONS

📚 8.7-8.8 HTTP 상태 코드

강의 포인트

상태 코드 분류

1xx: 정보 (Information)       - 거의 사용 안 함
2xx: 성공 (Success)           - 요청 성공
3xx: 리다이렉션 (Redirection)  - 추가 조치 필요
4xx: 클라이언트 에러           - 클라이언트 잘못
5xx: 서버 에러                - 서버 잘못

2xx - 성공

200 OK ⭐

// 요청 성공
GET /users  200 OK + 사용자 목록
POST /login  200 OK + 로그인 정보

201 Created

// 리소스 생성 성공
POST /users  201 Created + Location: /users/123

204 No Content

// 성공했지만 반환할 내용 없음
DELETE /users/123  204 No Content
PUT /users/123  204 No Content

3xx - 리다이렉션

301 Moved Permanently

영구 이동 (URL이 바뀜)
GET /old-url → 301 + Location: /new-url

검색 엔진도 URL 업데이트

302 Found

임시 이동
GET /login → 302 + Location: /dashboard

원래 URL은 유효함

304 Not Modified

캐시된 리소스 사용 (변경 없음)
GET /image.jpg → 304 Not Modified

브라우저 캐시에서 가져옴

4xx - 클라이언트 에러

400 Bad Request ⭐

// 잘못된 요청 (문법 오류, 유효하지 않은 데이터)
POST /users
Body: { "email": "잘못된 이메일" }
 400 Bad Request

401 Unauthorized ⭐

// 인증 필요 (로그인 안 함)
GET /my-profile
 401 Unauthorized
 "인증이 필요합니다. 로그인하세요."

403 Forbidden ⭐

// 권한 없음 (로그인은 했지만 접근 불가)
DELETE /users/999  (다른 사용자 삭제 시도)
 403 Forbidden
 "권한이 없습니다."

404 Not Found ⭐⭐⭐

// 리소스를 찾을 수 없음
GET /users/99999  (존재하지 않는 사용자)
 404 Not Found
 "사용자를 찾을 수 없습니다."

409 Conflict

// 충돌 (중복 등)
POST /users
Body: { "email": "existing@example.com" }
 409 Conflict
 "이미 사용 중인 이메일입니다."

5xx - 서버 에러

500 Internal Server Error ⭐

// 서버 내부 오류 (코드 버그, 예외 등)
GET /users
 500 Internal Server Error
 "서버 오류가 발생했습니다."

502 Bad Gateway

게이트웨이/프록시 서버 오류
서버가 유효하지 않은 응답을 받음

503 Service Unavailable

서비스 이용 불가 (서버 다운, 유지보수)
GET /api/data
→ 503 Service Unavailable
→ Retry-After: 3600 (1시간 후 재시도)

주요 상태 코드 암기

반드시 외워야 할 코드:

200 OK              - 성공
201 Created         - 생성 성공
204 No Content      - 성공 (본문 없음)

301 Moved Permanently - 영구 이동
302 Found           - 임시 이동
304 Not Modified    - 캐시 사용

400 Bad Request     - 잘못된 요청
401 Unauthorized    - 인증 필요
403 Forbidden       - 권한 없음
404 Not Found       - 없음
409 Conflict        - 충돌

500 Internal Server Error - 서버 오류
502 Bad Gateway     - 게이트웨이 오류
503 Service Unavailable - 서비스 불가

📚 8.9 HTTP 헤더

강의 포인트

일반 헤더 (General)

Date: Tue, 15 Jan 2024 10:00:00 GMT   # 메시지 생성 시간
Connection: keep-alive                 # 연결 유지

요청 헤더 (Request)

Host ⭐ (필수)

Host: api.example.com
→ 요청 대상 서버 (가상 호스팅)

User-Agent

User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64)...
→ 클라이언트 정보 (브라우저, OS 등)

Accept ⭐

Accept: application/json, text/html, */*
→ 클라이언트가 받을 수 있는 타입

Authorization ⭐

Authorization: Bearer eyJhbGciOiJIUzI1NiIs...
→ 인증 토큰
Cookie: session=abc123; user_id=456
→ 쿠키 전송

응답 헤더 (Response)

Content-Type ⭐⭐⭐

Content-Type: application/json; charset=UTF-8
→ 응답 본문의 타입

주요 타입:
- application/json      # JSON
- text/html             # HTML
- text/plain            # 텍스트
- image/png             # PNG 이미지
- multipart/form-data   # 파일 업로드

Content-Length

Content-Length: 1234
→ 본문 크기 (바이트)
Set-Cookie: session=abc123; Path=/; HttpOnly; Secure
→ 쿠키 설정

Location

Location: /users/123
→ 리다이렉션 대상 URL (3xx에서 사용)

Allow

Allow: GET, POST, PUT, DELETE
→ 지원하는 메서드 (OPTIONS 응답)

캐시 헤더

Cache-Control ⭐

Cache-Control: no-cache               # 캐시 사용 전 재검증
Cache-Control: no-store               # 캐시 저장 안 함
Cache-Control: max-age=3600           # 3600초 동안 캐시
Cache-Control: public                 # 모든 캐시 가능
Cache-Control: private                # 브라우저만 캐시

ETag

ETag: "33a64df551425fcc55e4d42a148795d9f25f89d4"
→ 리소스 버전 (변경 감지)

CORS 헤더

Access-Control-Allow-Origin ⭐

Access-Control-Allow-Origin: *
Access-Control-Allow-Origin: https://example.com
→ CORS 허용 출처

Access-Control-Allow-Methods

Access-Control-Allow-Methods: GET, POST, PUT, DELETE
→ CORS 허용 메서드

Access-Control-Allow-Headers

Access-Control-Allow-Headers: Content-Type, Authorization
→ CORS 허용 헤더

📚 8.10-8.12 쿠키, 세션, 스토리지

강의 포인트

// 쿠키 설정 (서버)
Set-Cookie: user_id=123; Max-Age=3600; Path=/; HttpOnly; Secure

// 쿠키 전송 (클라이언트 → 서버)
Cookie: user_id=123

// JavaScript로 쿠키 설정
document.cookie = "name=홍길동; max-age=3600; path=/";

// JavaScript로 쿠키 읽기
console.log(document.cookie);  // "name=홍길동; session=abc123"

쿠키 속성:

Max-Age=3600        # 3600초 후 만료
Expires=날짜        # 특정 날짜에 만료
Path=/              # 경로 지정
Domain=.example.com # 도메인 지정
Secure              # HTTPS에서만 전송
HttpOnly            # JavaScript 접근 불가 (보안)
SameSite=Strict     # CSRF 방지

사용 예:

세션 (Session)

세션 = 서버에 저장되는 사용자 상태

1. 클라이언트 로그인
2. 서버가 세션 생성 (session_id=abc123)
3. 서버가 쿠키로 session_id 전송
4. 클라이언트는 이후 요청마다 session_id 쿠키 전송
5. 서버는 session_id로 사용자 식별

쿠키 vs 세션:

쿠키:
✅ 클라이언트에 저장
✅ 빠름 (서버 요청 불필요)
❌ 보안 취약 (노출 가능)
❌ 용량 제한 (4KB)

세션:
✅ 서버에 저장
✅ 보안 좋음
❌ 서버 부하
❌ 서버 재시작 시 초기화

로컬 스토리지 (Local Storage)

// 저장 (영구 보관)
localStorage.setItem('name', '홍길동');
localStorage.setItem('settings', JSON.stringify({ theme: 'dark' }));

// 읽기
const name = localStorage.getItem('name');
const settings = JSON.parse(localStorage.getItem('settings'));

// 삭제
localStorage.removeItem('name');
localStorage.clear();  // 전체 삭제

// 키 목록
for (let i = 0; i < localStorage.length; i++) {
    const key = localStorage.key(i);
    console.log(key, localStorage.getItem(key));
}

특징:

세션 스토리지 (Session Storage)

// 저장 (탭 닫으면 삭제)
sessionStorage.setItem('temp', 'temporary data');

// 읽기
const temp = sessionStorage.getItem('temp');

// API는 localStorage와 동일

특징:

비교표

항목 Cookie Local Storage Session Storage
용량 4KB 5-10MB 5-10MB
만료 설정 가능 영구 탭 닫을 때
서버 전송 ✅ 자동 ❌ 수동 ❌ 수동
접근 HTTP + JS JS만 JS만
보안 HttpOnly 설정 가능 보통 보통

사용 가이드

Cookie:
- 로그인 세션 (HttpOnly, Secure)
- 추적/분석

Local Storage:
- 사용자 설정
- 오프라인 데이터
- 캐시

Session Storage:
- 폼 입력 임시 저장
- 페이지 간 데이터 전달 (같은 탭)

📚 8.13-8.14 HTTPS와 CORS

HTTPS (HTTP Secure)

HTTPS = HTTP + SSL/TLS (암호화)

HTTP:  클라이언트 ↔ 서버 (평문 전송) ❌
HTTPS: 클라이언트 ↔ SSL/TLS ↔ 서버 (암호화) ✅

SSL/TLS 역할:

  1. 암호화 (도청 방지)
  2. 인증 (위장 방지)
  3. 무결성 (변조 방지)

HTTPS 인증서:

인증 기관 (CA)이 발급
- Let's Encrypt (무료)
- DigiCert, Comodo 등 (유료)

브라우저가 인증서 검증
✅ 유효 → 자물쇠 표시
❌ 무효 → 경고 표시

CORS (Cross-Origin Resource Sharing)

CORS = 다른 출처(도메인)의 리소스에 접근하기 위한 메커니즘

출처 = 프로토콜 + 도메인 + 포트

https://example.com:443/api/users
  ↓         ↓        ↓
프로토콜   도메인    포트

Same-Origin (같은 출처):

https://example.com/page1
https://example.com/page2
→ 같은 출처 ✅

https://example.com
https://api.example.com
→ 다른 출처 ❌ (서브도메인 다름)

http://example.com
https://example.com
→ 다른 출처 ❌ (프로토콜 다름)

CORS 에러:

// https://mysite.com에서
fetch('https://api.example.com/data')
// ❌ CORS Error!

// 콘솔 에러:
Access to fetch at 'https://api.example.com/data' from origin 
'https://mysite.com' has been blocked by CORS policy

CORS 해결 (서버 설정):

// Node.js + Express
app.use((req, res, next) => {
    res.header('Access-Control-Allow-Origin', '*');  // 모든 출처 허용
    res.header('Access-Control-Allow-Origin', 'https://mysite.com');  // 특정 출처만
    res.header('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE');
    res.header('Access-Control-Allow-Headers', 'Content-Type, Authorization');
    next();
});

Preflight 요청:

복잡한 요청 전에 OPTIONS 요청으로 허용 여부 확인

1. Preflight (OPTIONS)
   브라우저: "POST 요청 보내도 되나요?"
   서버: "네, 허용합니다"

2. 실제 요청 (POST)
   브라우저: 데이터 전송
   서버: 응답

🎯 강의 진행 팁

1-2교시: HTTP 기초 (2시간)

3-4교시: 상태 코드와 헤더 (2시간)

5-6교시: 쿠키, 세션, 스토리지 (2시간)

7-8교시: HTTPS와 CORS (2시간)


📝 실습: REST API 클라이언트 만들기

class APIClient {
    constructor(baseURL) {
        this.baseURL = baseURL;
    }
    
    async request(endpoint, options = {}) {
        const url = `${this.baseURL}${endpoint}`;
        
        const config = {
            ...options,
            headers: {
                'Content-Type': 'application/json',
                ...options.headers
            }
        };
        
        try {
            const response = await fetch(url, config);
            
            if (!response.ok) {
                throw new Error(`HTTP ${response.status}: ${response.statusText}`);
            }
            
            return await response.json();
            
        } catch (error) {
            console.error('API Error:', error);
            throw error;
        }
    }
    
    get(endpoint) {
        return this.request(endpoint);
    }
    
    post(endpoint, data) {
        return this.request(endpoint, {
            method: 'POST',
            body: JSON.stringify(data)
        });
    }
    
    put(endpoint, data) {
        return this.request(endpoint, {
            method: 'PUT',
            body: JSON.stringify(data)
        });
    }
    
    delete(endpoint) {
        return this.request(endpoint, {
            method: 'DELETE'
        });
    }
}

// 사용
const api = new APIClient('https://jsonplaceholder.typicode.com');
api.get('/users').then(users => console.log(users));

8장 HTTP 통신을 완료했습니다! 🌐