🌎Http

HTTP: HTTP와 TCP 관계

limdaeil 2026. 1. 13. 15:50

🔖Contents

1. HTTP

HTTP(HyperText Transfer Protocol)는 웹에서 클라이언트와 서버가 데이터를 주고받기 위해 사용하는 애플리케이션 계층의 통신 규약입니다. 우리가 웹 브라우저에서 주소를 입력하고 페이지를 보는 모든 과정은 HTTP 통신을 기반으로 이루어집니다. HTTP의 가장 기본적인 목적은 단순합니다. 클라이언트가 원하는 리소스를 서버에 요청하고, 서버는 그 요청에 대한 결과를 응답하는 것입니다. 이때 리소스는 HTML 문서일 수도 있고, 이미지, JSON 데이터, 파일 등 다양한 형태가 될 수 있습니다. HTTP는 자체적으로 데이터를 전송하지는 않습니다. 실제 데이터 전송은 하위 계층인 TCP/IP 위에서 이루어지며, HTTP는 그 위에서 “어떤 형식으로 데이터를 주고받을지”를 정의하는 역할을 합니다.

1.1 HTTP는 메시지 기반 프로토콜

HTTP 통신은 기본적으로 HTTP 메시지 요청(Request)과 응답(Response) 구조로 이루어집니다.

  1. 클라이언트가 서버로 요청(Request) 메시지를 보냄
  2. 서버는 요청을 처리한 뒤 응답(Response) 메시지를 반환
  3. 응답이 완료되면 연결은 종료

이때 HTTP는 연결 자체를 정의하지 않습니다. 오직 “어떤 형식의 메시지를 주고받을 것인가”만을 규정합니다. 연결은 TCP가 담당하고, 암호화는 TLS가 담당하며 HTTP는 메시지의 구조와 의미를 담당합니다. HTTP는 모든 통신이 클라이언트의 요청으로 시작된다는 점입니다. 서버는 스스로 데이터를 보내지 않으며, 반드시 클라이언트의 요청이 있어야만 응답을 보낼 수 있습니다. 이러한 특성은 웹을 단순하고 안정적으로 만들었지만, 실시간성이 요구되는 상황에서는 명확한 한계 작용합니다.

 

HTTP 메시지 구조

HTTP 메시지는 크게 세 부분으로 구성됩니다. 이 구조는 모든 HTTP 요청과 응답에서 동일하게 적용됩니다. HTTP 메시지 구조는 단순해 보이지만, 웹 통신의 모든 개념이 이 구조 위에 쌓여 있습니다. 메서드는 Start Line에서 표현되고 인증과 캐시는 Header로 제어되며 실제 데이터는 Body로 전달됩니다. 따라서 HTTP 메시지 구조를 정확히 이해하면, 개발 인생에서 계속 등장하는 HTTP 메서드, 상태 코드, 인증, 캐시, REST API 설계가 자연스럽게 연결됩니다.

1.2 HTTP 요청 메시지

1. Start Line (요청 라인)

서버는 이 Start Line을 통해 “어떤 자원에 대해, 어떤 행위를, 어떤 규칙으로 수행해야 하는지”를 즉시 파악합니다.

GET /index.html HTTP/1.1
  • 요청 메시지의 첫 줄을 Start Line 또는 Request Line
  • HTTP 메서드, 요청 대상 URI, HTTP 버전으로 구성

2. Headers (헤더)

헤더는 요청에 대한 추가 정보, 즉 메타데이터(metadata)를 전달하는 영역입니다. 요청의 유형(Content-Type), 응답의 유형(Accept), 인증 정보(Authorization), 캐싱 제어(Cache-Control, ETag, Last-Modified) 등 다양한 메타데이터가 포함됩니다.

Host: example.com
Content-Type: application/json
Authorization: Bearer eyJ...
  • Key: Value 형식으로 작성
  • 요청 본문이 아니라, 요청을 설명하고 보조하는 정보
  • 서버는 헤더를 참고하여 요청을 해석하고 처리 방식을 결정

자주 사용되는 요청 헤더 예시

  • Host: 요청이 전달될 대상 서버의 도메인(HTTP/1.1에서는 필수 헤더)
  • Content-Type: Body에 포함된 데이터의 형식을 명시
  • Accept: 클라이언트가 이해 가능한 컨텐츠 타입이 무엇인지 전달
  • Authorization: 인증 및 인가 정보를 전달

헤더는 통신을 제어하는 설명서 역할을 수행합니다. 다양한 헤더 종류는 3. 부록에 정리했습니다.

 

3. Body (요청 본문, 선택)

Body는 실제로 서버에 전달하고자 하는 데이터를 담는 영역입니다.

{
  "email": "user@example.com",
  "password": "1234"
}
  • 주로 데이터 생성·수정 요청에서 사용됩니다.
  • 데이터 형식은 Content-Type 헤더에 의해 해석됩니다.

서버는 Body 자체만 보지 않고 헤더와 함께 Body를 해석합니다.

1.3 HTTP 응답 메시지

1. Start Line (요청 라인)

클라이언트는 이 상태 라인을 통해 요청이 성공했는지, 실패했는지, 추가 동작이 필요한지를 판단합니다.

HTTP/1.1 200 OK
  • 요청 메시지의 첫 줄을 Start Line 또는 Request Line
  • HTTP 버전, 상태 코드 (숫자), 상태 메시지 (사람을 위한 설명)으로 구성

2. Headers (응답 헤더)

응답 헤더는 클라이언트가 응답 Body를 올바르게 처리하기 위한 정보를 제공합니다. 응답 헤더 역시 메타데이터입니다.

Content-Type: application/json
Content-Length: 256
  • 응답 데이터의 형식 설명
  • 데이터 길이 전달
  • 캐시 정책 전달
  • 쿠키 설정

3. Body (응답 본문)

응답 Body에는 실제 응답 결과 데이터가 담깁니다. HTML 문서, JSON 데이터, 이미지, 파일 등을 응답 Body 안에 포함합니다. 클라이언트는 응답 헤더를 참고하여 이 Body를 화면에 표시하거나, 로직에 활용합니다. Header와 Body의 역할은 명확히 구분할 수 있습니다. 단, 헤더 없이는 바디를 정확히 해석할 수 없으며, 바디 없이 헤더만으로도 의미 있는 통신이 가능합니다.

  • Header: 데이터에 대한 설명과 제어 정보
  • Body: 실제 전달하려는 데이터

1.4 HTTP 메서드

RFC 9110

HTTP 메서드는 클라이언트가 웹 서버에게 특정 리소스에 대해 어떤 동작(조회, 생성, 수정, 삭제 등)을 수행하고 싶은지를 나타내는 요청 방식으로, 클라이언트-서버 간 통신의 핵심적인 약속입니다. 같은 URI(/users/1)라도 메서드가 GET인지 DELETE인지에 따라 서버가 수행해야 할 작업이 완전히 달라집니다. 따라서 HTTP 메서드는 요청의 의미(semantic)를 결정하고, 서버와 클라이언트가 서로를 오해하지 않도록 하는 계약(Contract) 역할을 합니다. 특히 REST API를 설계할 때는 메서드를 올바르게 사용하는 것만으로도 API가 훨씬 읽기 쉽고 예측 가능해집니다.

GET /v1/users/1 HTTP/1.1

HTTP 요청의 시작 줄(Start Line)의 GET이 바로 HTTP 메서드이며, 서버는 이 메서드를 보고 결정합니다.\

표준 HTTP 메서드 목록과 핵심 용도

HTTP 표준에는 다양한 메서드가 있지만, 실무 API에서 핵심은 다음입니다.

  • GET: 자원 조회
  • POST: 자원 생성, 처리 요청(“서버에게 작업을 맡김”)
  • PUT: 자원의 전체 대체(업서트 성격 포함 가능)
  • PATCH: 자원의 부분 수정
  • DELETE: 자원 삭제
  • HEAD: 본문 없이 헤더만 조회(존재 여부/캐시 검증 등에 사용)
  • OPTIONS: 서버가 지원하는 통신 옵션 확인(CORS 사전 요청 등)
  • “안전(Safe)”과 “멱등(Idempotent)”은 메서드 이해의 핵심

HTTP 메서드의 안전과 멱등성 두 가지 성질을 반드시 이해해야만 깊게 학습할 수 있습니다.

  1. 안전 메서드(Safe Method): 서버의 상태를 변경하지 않는 메서드를 의미
    • 안전(Safe): GET, HEAD, OPTIONS (대표적으로)
    • 안전하지 않음(not Safe): POST, PUT, PATCH, DELETE
    여기서 중요한 포인트는 “안전”이 실제로 변경이 절대 안 일어난다는 보장이 아니라,HTTP 의미상 서버 상태 변경을 의도하지 않는다는 계약에 가깝습니다. 따라서 GET 요청으로 DB가 변경되도록 구현하면, HTTP 설계 철학과 어긋나며 캐시, 프록시, 브라우저 동작에서도 문제가 생길 수 있습니다.
  2. 멱등(Idempotent): 같은 요청을 여러 번 수행해도 결과가 같아지는 성질
    • 멱등: GET, PUT, DELETE, HEAD, OPTIONS
    • 비멱등: POST (대표적으로)
    예를 들어, DELETE /users/1을 여러 번 호출해도 최종 상태는 “삭제됨”으로 동일합니다. (이미 삭제된 경우 404, 204 등 정책 차이는 있어도 “삭제 상태”는 유지됩니다.) PUT /users/1은 “자원을 특정 상태로 만든다”는 의미이므로 여러 번 호출해도 결과가 같습니다. 반면 POST /orders는 호출할 때마다 주문이 새로 생성될 수 있어 결과가 달라질 수 있습니다. 멱등성은 실무에서 특히 중요합니다.네트워크 타임아웃이나 재시도(retry)가 발생했을 때, 서버가 같은 요청을 중복 처리하지 않도록 설계를 유도하기 때문입니다.

1. GET 조회(읽기)

  • GET자원을 조회하는 메서드
  • 서버 상태를 바꾸지 않는 것이 원칙(Safe)
  • 같은 요청을 반복해도 결과가 같아야 함(멱등)

GET 예시

  • 목록 조회: GET /users
  • 단건 조회: GET /users/1
  • 검색/필터: GET /users?role=ADMIN&keyword=kim

GET에 Body 사용을 지양합니다.
표준상 GET에 Body가 “금지”된 것은 아니지만, 실무에서는 거의 쓰지 않습니다. 이유는 명확합니다.

  1. 일관된 지원이 어렵습니다
    서버, 프록시, 캐시, 라이브러리 중 일부는 GET Body를 정상 처리하지 않거나 무시할 수 있습니다.
  2. 캐시와의 궁합 문제
    GET은 주로 캐시 대상인데, 캐시는 일반적으로 URI(쿼리스트링 포함)를 키로 삼습니다.
    Body 기반 조회는 캐시 설계에 불리합니다.
  3. 의도 표현이 어색합니다
    조회 조건이 복잡하면 POST /search 같은 형태가 더 일관적일 수 있습니다(아래에서 자세히 다룹니다).

결론적으로, 조회 조건은 쿼리 파라미터로 표현하는 것이 정석입니다.
매우 복잡한 검색은 POST를 검색 엔드포인트에 쓰는 설계가 실무에서 자주 등장합니다.

 

2. POST 생성 또는 “처리 요청”

POST는 가장 오해가 많은 메서드입니다. 흔히 “POST = 생성”이라고 외우지만, 정확히는 다음과 같습니다.

  • 서버에게 생성이 아니라 처리(Processing)를 요청하는 메서드
  • 그 결과로 새 자원이 생성될 수도 있고, 단순히 작업이 수행될 수도 있음
  • 일반적으로 비멱등(호출할 때마다 결과가 달라질 수 있음)

(1) 자원 생성

  • POST /orders → 주문 생성
  • 성공 시 보통 201 Created + Location 헤더로 생성된 자원 URI 제공

(2) 서버 작업 실행(커맨드)

  • POST /payments/confirm → 결제 승인 처리
  • POST /users/1/reset-password → 비밀번호 초기화 작업

(3) 복잡한 검색

  • POST /users/search → 검색 조건이 너무 복잡하거나, 보안상/길이 제한상 쿼리스트링이 불리한 경우

POST를 쓸 때 주의점

  • 재시도 시 중복 생성 위험이 있으므로, 중요한 생성 요청에서는 멱등 키(Idempotency-Key) 설계를 고려합니다.
  • “생성”이라면 응답으로 201 Created를 반환하는 것이 의미적으로 명확합니다.

3. PUT 전체 대체(Replace)

PUT은 자원을 “이 상태로 만들어라”에 가깝습니다.

  • 대상 URI가 명확해야 합니다: PUT /users/1
  • 요청 본문은 자원의 “전체 표현”이 되는 것이 정석입니다.
  • 멱등입니다.

PUT 사용 시점

  • 사용자의 전체 정보 갱신: PUT /users/1
  • 서버가 “현재 상태를 이 내용으로 완전히 교체”하도록 요구할 때

PUT의 실무적 함정

PUT을 “부분 수정”처럼 쓰면, 의도가 흐려지고 예상치 못한 필드 누락 문제가 생길 수 있습니다. 예를 들어 PUT /users/1{"name":"Kim"}만 보내면, 정석 의미상 “나머지 필드는 없다는 의미”가 될 수 있습니다. 실무에서는 이를 방지하기 위해 PUT은 전체 DTO를 받거나, 부분 수정을 PATCH로 분리하는데, 고유 식별자를 포함한 모든 필드를 수정하는 PUT의 사용은 극히 드물어요.

 

4. PATCH 부분 수정(Partial Update)

PATCH는 자원의 일부만 변경하는 메서드입니다.
부분 수정이므로 Body는 “변경점” 중심으로 멱등이 “될 수도 있고 아닐 수도 있습니다.” (구현 방식에 따라 다름)

PATCH 사용 시점

  • 특정 필드만 변경: PATCH /users/1 (예: 이름만)
  • 상태 변경: PATCH /orders/1 (예: 주문 상태 변경)

PATCH 설계 방식 2가지

  • (1) Partial Object 방식(실무에서 가장 흔함)
    • 보내는 필드만 업데이트
    • null 처리 규칙(“null이면 삭제인가?” 등)을 명확히 문서화해야 합니다.
  • { "name": "Kim", "phone": "010-..." }
  • (2) JSON Patch, Merge Patch(표준 패치 포맷)
    • JSON Patch: 작업 명령(add, replace, remove) 기반
    • Merge Patch: 덮어쓰기 규칙 기반
    • 장점: 변경 의도가 더 명확할 수 있음
    • 단점: 클라이언트 구현 부담이 커서 팀, 서비스 성격에 따라 선택합니다.

 

5. DELETE 삭제

DELETE는 자원을 삭제합니다. 멱등입니다.

DELETE 사용 시점

  • DELETE /users/1
  • 리소스 삭제 API에서 표준적으로 사용

실무 팁: “삭제”는 항상 물리 삭제가 아닐 수 있습니다

실무에서는 소프트 삭제(soft delete)가 흔합니다. DB에서는 삭제 플래그를 올리고 조회에서 제외하며 외부에는 “삭제된 것처럼” 보이게 합니다. 하지만 HTTP 의미는 “삭제 요청”이므로, 내부 구현이 물리, 논리 삭제인지와는 별개로 외부 계약(응답 코드, 재호출 시 동작)을 일관되게 유지하는 것이 중요합니다.

 

6. HEAD

GET과 동일한 의미지만, 응답 Body를 제외하고 헤더만 받습니다.

HEAD 사용 시점

  1. 리소스 존재 여부 확인
  2. 파일 다운로드 전에 길이(Content-Length) 확인
  3. 캐시 검증(ETag 등) 목적
HEAD https://www.example.com/api/users/1
HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8
Date: Tue, 13 Jan 2026 09:00:00 GMT

 

7. OPTIONS

특정 리소스에 대해 허용되는 HTTP 메소드(GET, POST, PUT 등)나 헤더 정보를 미리 파악하여 통신을 설정하고, 불필요한 오류를 줄이며 보안을 강화하기 위해 사용됩니다. 가장 대표적인 사용처는 CORS Preflight(사전 요청)입니다. 브라우저가 실제 요청 전에 “이 요청 보내도 되나요?”를 OPTIONS로 확인합니다.

OPTIONS https://example.com/api/resource
HTTP/1.1 200 OK
Allow: GET, POST, OPTIONS
Access-Control-Allow-Origin: *
Access-Control-Allow-Methods: GET, POST, OPTIONS

1.5 HTTP 상태 코드

HTTP에서 상태 코드(Status Code)는 단순한 숫자가 아니라 요청 처리 결과를 압축해서 전달하는 공식적인 언어입니다.
클라이언트는 HTTP 상태 코드를 통해 다음을 판단합니다.

  • 요청이 성공했는가?
  • 실패했다면, 내 잘못인가? 서버 문제인가?
  • 다시 시도해도 되는가?
  • 다른 URL로 이동해야 하는가?

상태 코드는 서버의 의사 표현 수단이며, 잘 설계된 상태 코드는 API의 품질을 그대로 드러냅니다.

상태 코드의 기본 철학

HTTP 상태 코드는 3자리 숫자로 구성되며, 첫 번째 숫자가 응답의 성격을 결정합니다.

범위 의미
1xx 처리 중 (거의 사용하지 않음)
2xx 성공
3xx 리다이렉션
4xx 클라이언트 오류
5xx 서버 오류

이 분류 자체가 HTTP의 철학을 보여줍니다.
“누가 잘못했는가?”를 명확히 구분한다. 구분은 이후 REST API 설계에서 매우 중요해집니다.

1. 2xx 성공 응답의 의미

  • 200 OK
    • 조회 성공
    • 수정 성공
    • 삭제 성공(Body가 있는 경우)
    HTTP/1.1 200 OK
  • 가장 일반적인 성공 응답입니다. “요청이 정상적으로 처리되었고, 응답 본문이 의미를 가집니다.
  • 201 Created
    • 보통 POST 요청의 성공 결과
    • Location 헤더에 생성된 자원의 URI를 포함하는 것이 정석입니다.
    HTTP/1.1 201 Created
    Location: /users/123
  • 새 자원이 생성되었을 때 사용합니다. 200과의 차이는 “새로운 자원이 생겼다”는 의미를 명확히 전달한다는 점입니다.
  • 204 No Content
    • DELETE 성공
    • PATCH, PUT 성공 후 응답 데이터가 불필요한 경우
    HTTP/1.1 204 No Content
  • 성공했지만 응답 본문이 필요 없는 경우입니다. “성공은 했지만, 보여줄 내용은 없습니다.”

3xx 리다이렉션

3xx 응답은 요청한 자원이 다른 위치에 있음을 알리는 신호입니다.

  • 301, 302
    • 301 Moved Permanently: 영구 이동
    • 302 Found: 임시 이동
    주로 브라우저 URL 이동, CDN, 도메인 변경 등에서 사용됩니다.
    API에서는 상대적으로 사용 빈도가 낮지만, HTTP의 ‘자원 중심 설계’ 철학을 보여주는 코드입니다.

4. 4xx 클라이언트 오류

4xx는 “요청 자체에 문제가 있다”는 의미입니다. 서버는 정상이고, 요청을 보낸 쪽이 잘못했습니다.

  • 400 Bad Request
    • JSON 파싱 실패
    • 필수 필드 누락
    • 타입 오류
  • 요청 형식이 잘못되었을 때 사용합니다.
  • 401 Unauthorized
    인증(Authentication) 실패입니다. 자주 헷갈리지만, 권한 부족이 아닙니다.
    • 로그인되지 않음
    • 토큰이 없거나 유효하지 않음
  • 403 Forbidden
     인가(Authorization) 실패입니다. “당신이 누구인지는 알지만, 이 자원은 허용되지 않습니다.”
    • 로그인은 되었으나 해당 자원에 접근할 권한이 없음
  • 404 Not Found
    요청한 자원이 존재하지 않습니다. REST에서 아주 중요한 코드입니다.“이 자원은 없다”는 의미를 명확히 전달합니다.
    • 잘못된 URI
    • 존재하지 않는 리소스 ID
  • 410 Gone
    자원이 과거에는 있었지만, 이제는 영구적으로 제거되었습니다.
    404보다 더 강한 의미를 가지며, RESTful 설계에서 자원의 생명주기를 표현할 수 있습니다.
  • 탈퇴한 사용자
  • 만료된 리소스

5xx 서버 오류

5xx는 서버가 요청을 정상적으로 처리하지 못했다는 의미입니다.
“서버 쪽에서 문제가 발생했습니다.”

  • 500 Internal Server Error
    가장 포괄적인 서버 오류입니다.
    • 예외 처리 누락
    • 예상치 못한 서버 내부 문제
  • 502 Bad Gateway
    중간 서버(게이트웨이, 프록시)가 뒤쪽 서버로부터 잘못된 응답을 받았을 때 사용합니다.
    • MSA 환경에서 자주 등장
  • 503 Service Unavailable
    서버가 일시적으로 요청을 처리할 수 없는 상태입니다.
    클라이언트는 “잠시 후 재시도”를 고려할 수 있습니다.
    • 서버 다운
    • 트래픽 폭주
    • 점검 중

상태 코드 설계 = API 설계 수준

같은 실패라도 상태 코드에 따라 의미가 완전히 달라집니다.
HTTP 상태 구분이 명확할수록 클라이언트 로직이 단순해지고 디버깅이 쉬워지며 API의 신뢰도가 올라갑니다.

  • 400: 요청이 잘못됨
  • 401: 인증 필요
  • 403: 권한 없음
  • 404: 자원 없음
  • 500: 서버 문제

1.6 RESTful

RESTful이란 REST(Representational State Transfer)라는 아키텍처 스타일의 설계 규칙을 잘 따르는 웹 서비스를 의미하며, HTTP와 URI를 기반으로 자원(Resource)에 접근하여 데이터를 주고받는 방식입니다. 이는 API 주소만 보고도 어떤 자원을 요청하는지 직관적으로 알 수 있고, 단순한 HTTP 요청으로 통신할 수 있어 확장성과 범용성이 높다는 장점이 있어 현대 웹 서비스에서 널리 사용됩니다.

  • REST: Roy Fielding이 제시한 아키텍처 스타일
  • REST API: REST 원칙을 참고하여 만든 API
  • RESTful: REST의 설계 원칙을 의도적으로 잘 지킨 시스템

따라서 RESTful은 특정 기술이나 프레임워크가 아니라 설계 품질에 대한 평가입니다.

  • REST를 “썼다” ≠ RESTful하다
  • REST 원칙을 “잘 활용했다” = RESTful하다

REST 정리

  1. HTTP URI(Uniform Resource Identifier)를 통해 자원(Resource)을 명시하고,
  2. HTTP Method(POST, GET, PUT, DELETE, PATCH 등)를 통해
  3. 해당 자원(URI)에 대한 CRUD Operation을 적용하는 것을 의미합니다.

REST 구성 3 개 요소

  1. 자원(Resource) : HTTP URI
  2. 자원에 대한 행위(Verb) : HTTP Method
  3. 자원에 대한 행위의 내용 (Representations) : HTTP Message Pay Load

REST의 특징

  1. Server-Client(서버-클라이언트 구조)
  2. Stateless(무상태)
  3. Cacheable(캐시 처리 가능)
  4. Layered System(계층화)
  5. Uniform Interface(인터페이스 일관성)

REST의 장점

  • 기존 HTTP 인프라를 그대로 활용할 수 있습니다.
    별도의 통신 프로토콜이나 추가 인프라 없이 웹 서버, 프록시, 캐시, 로드 밸런서를 그대로 사용할 수 있습니다.
  • HTTP 표준의 의미 체계를 적극 활용할 수 있습니다.
    메서드, 상태 코드, 헤더를 통해 요청의 의도와 결과를 명확하게 표현할 수 있습니다.
  • 플랫폼과 언어에 독립적입니다.
    HTTP를 지원하는 모든 환경에서 동일한 방식으로 사용할 수 있어 범용성이 높습니다.
  • API의 의도가 직관적입니다.
    자원 중심 URI와 메서드 조합만으로도 어떤 요청인지 쉽게 파악할 수 있습니다.
  • 서버와 클라이언트의 역할이 명확히 분리됩니다.
    서버는 자원과 비즈니스 로직을, 클라이언트는 표현과 사용자 인터랙션을 담당합니다.

REST의 단점

  • 엄격한 표준이 존재하지 않습니다.
    REST는 명세가 아닌 설계 원칙이므로, 팀마다 해석과 구현 방식이 달라질 수 있습니다.
  • 모든 도메인을 CRUD로 표현하기 어렵습니다.
    결제, 상태 전이, 워크플로우처럼 행위 중심 도메인에서는 설계가 어색해질 수 있습니다.
  • 설계 난이도가 생각보다 높습니다.
    자원 모델링, 상태 코드, 에러 정책을 함께 고민해야 하며 단순히 URI만 정한다고 해결되지 않습니다.
  • 고성능·저지연 통신에는 한계가 있습니다.
    내부 서비스 간 초고속 통신이나 스트리밍에는 gRPC 등 다른 방식이 더 적합할 수 있습니다.

RESTful은 HTTP를 가장 의미 있게 사용하는 설계 방식이지만, 모든 상황에 대한 만능 해법은 아닙니다.

 

REST API 설계 규칙 정리

REST API는 단순히 HTTP를 사용한다고 해서 RESTful해지는 것이 아닙니다. URI와 메서드를 일관된 규칙으로 설계했을 때 비로소 REST의 장점을 살릴 수 있습니다. 아래는 REST API 설계 시 가장 기본적으로 지켜야 할 규칙들입니다.

 

1. URI에는 동사보다 명사를 사용합니다

URI는 “무엇을 할지”가 아니라 “무엇에 대한 요청인지(자원)”를 나타내야 합니다.
행위는 URI가 아니라 HTTP 메서드(GET, DELETE 등)가 표현해야 합니다.

❌ Bad
/getUser
/deletePost/1
✅ Good
/users
/posts/1

 

2. URI는 소문자로 작성합니다

대소문자가 섞이면 가독성이 떨어지고, 운영 환경에 따라 혼란이 생길 수 있습니다.
REST API에서는 소문자 사용이 관례입니다.

❌ Bad
/Running
✅ Good
/running

 

3. URI 끝에 슬래시(/)를 붙이지 않습니다

슬래시 유무에 따라 서로 다른 자원으로 인식될 수 있으므로,
하나의 자원은 하나의 URI로 표현하는 것이 좋습니다.

❌ Bad
/test/
✅ Good
/test

 

4. 단어 구분에는 언더바(_) 대신 하이픈(-)을 사용합니다

하이픈은 사람이 읽기에 자연스럽고, URI 표준 관례에 더 가깝습니다.
언더바는 가독성이 떨어질 수 있습니다.

❌ Bad
/test_blog
✅ Good
/test-blog

 

5. 파일 확장자를 URI에 포함하지 않습니다

응답 데이터의 형식(JSON, HTML, 이미지 등)은
URI가 아니라 HTTP 헤더(Content-Type)로 구분해야 합니다.

❌ Bad
/photo.jpg
✅ Good
/photo

 

6. URI에 행위(동작)를 포함하지 않습니다

삭제, 수정과 같은 동작은 URI가 아니라
HTTP 메서드(DELETE, PATCH 등)가 담당합니다.

❌ Bad
/delete-post/1
/update-user
✅ Good
/posts/1
/users/1

 

7. 메서드와 함께 보면 의도가 가장 명확해집니다

GET    /users/1     // 사용자 조회
POST   /users       // 사용자 생성
PATCH  /users/1     // 사용자 수정
DELETE /users/1     // 사용자 삭제

URI는 자원,메서드는 행위, 상태 코드는 결과를 표현합니다.
REST API 설계의 핵심은 “URI는 자원만 표현하고, 행위는 HTTP 메서드에 맡기는 것”입니다.

 

RESTful API에서 예외적으로 동사가 허용되는 경우

RESTful API의 기본 원칙은 URI에 동사를 쓰지 않는다는 것입니다. 하지만 실무에서는 이 원칙을 무조건적으로 지키기 어려운 경우가 분명히 존재합니다. 중요한 것은 “동사를 쓰느냐 마느냐”가 아니라 “자원으로 표현할 수 있는가, 없는가”입니다.

 

1. 자원으로 표현하기 어려운 “행위 중심 도메인”

REST는 CRUD 중심 설계에 매우 적합하지만, 다음과 같은 경우에는 행위 자체가 핵심 의미가 됩니다.

  • 결제 승인
  • 주문 취소
  • 비밀번호 재설정
  • 이메일 인증
  • 상태 전이(approve, reject 등)

이런 경우를 억지로 자원으로만 표현하면 오히려 가독성이 떨어집니다.

❌ 어색한 REST
POST /payments/status
PATCH /orders/1
✅ 의도가 명확한 설계
POST /payments/confirm
POST /orders/1/cancel

이 경우 동사는 “리소스에 대한 액션”으로 해석되며, REST를 깨기보다는 REST의 한계를 현실적으로 보완한 설계에 가깝습니다.

 

2. 상태 전이가 중요한 경우

상태가 명확히 정의된 도메인에서는 “값 변경”보다 “어떤 상태로 전이되었는가”가 더 중요합니다.

  • 주문 → 결제 대기 → 결제 완료 → 배송 중 → 완료

이때 단순 PATCH로 상태 값을 바꾸는 것보다, 의도를 드러내는 액션 URI가 더 읽기 쉬운 경우가 많습니다.

❌ 의미가 불명확한 설계
PATCH /orders/1
{ "status": "CANCELLED" }
✅ 의도가 드러나는 설계
POST /orders/1/cancel
POST /orders/1/complete

상태 전이 API는 행위 기반 설계가 오히려 명확할 수 있습니다.

 

3. 서버에서 “작업 실행”이 목적일 때

REST는 자원 중심이지만, 서버에게 어떤 작업을 실행하라고 요청하는 경우도 많습니다.

  • 배치 실행
  • 캐시 초기화
  • 통계 집계
  • 리포트 생성

이 경우 생성되는 자원이 없거나, 결과가 즉시 반환되지 않는 경우가 많습니다.

POST /reports/generate
POST /cache/refresh

이 역시 “완벽한 REST”보다는 명확한 의도 전달을 우선한 설계로 보는 것이 현실적입니다.

 

실무에서 REST를 어디까지 지켜야 하는가

REST는 절대적인 규칙이 아니라 설계 지침입니다. 실무에서는 다음 기준을 잡는 것이 가장 현실적입니다.

 

1. 기본 CRUD는 반드시 RESTful하게

다음 영역만큼은 타협하지 않는 것이 좋습니다.

  • 조회: GET /resources
  • 생성: POST /resources
  • 수정: PATCH /resources/{id}
  • 삭제: DELETE /resources/{id}

이 부분이 흔들리면 API 전체의 일관성이 무너지고 클라이언트, 서버 간 커뮤니케이션 비용이 급격히 증가합니다.

 

2. REST를 지키느라 가독성을 해치지 말 것

REST 원칙을 과하게 적용하면, 오히려 의도가 숨겨지는 API가 되기도 합니다. REST를 위한 REST는 피해야 합니다.

  • 팀원이 URL만 보고 이해하기 어려운가?
  • API 문서를 보지 않으면 추론이 불가능한가?

그렇다면 설계가 과도했을 가능성이 큽니다.

 

3. “일관성”이 “순수성”보다 중요합니다

완벽하게 RESTful한 API보다 중요한 것은 프로젝트 내부에서의 일관성입니다.

  • 동사를 허용한다면, 허용 기준을 명확히
  • 상태 전이 API는 일관된 패턴으로
  • POST를 액션용으로 쓴다면 명확한 규칙으로

팀 내에서 예측 가능한 API가 외부에서 보기에 RESTful한 API보다 더 가치 있는 경우가 많습니다.

 

4. REST는 외부 API에 더 적합합니다

REST는 다음과 같은 상황에서 특히 강점을 가집니다.

  • 외부에 공개되는 API
  • 여러 클라이언트(Web, Mobile, Third-party)가 사용하는 API
  • 장기간 유지보수가 필요한 서비스

반대로, 내부 MSA 통신과 고성능·저지연 요구, 강한 타입 계약이 필요한 경우에는 gRPC, 메시지 기반 통신이 더 적합할 수 있습니다. REST는 반드시 지켜야 할 규칙이 아니라, “일관성과 의도를 가장 잘 드러내기 위한 기준”입니다. 기본 CRUD는 RESTful하고, 행위 중심 도메인은 예외적으로, 무엇보다 읽기 쉬운 API를 목표로 합니다.

1.7 HTTP의 핵심 특징

HTTP를 이해할 때 반드시 짚고 넘어가야 할 HTTP의 핵심 특징은 다음과 같습니다.

1. 클라이언트-서버 모델: HTTP는 클라이언트와 서버의 역할이 명확히 분리된 구조를 가집니다. 클라이언트는 요청을 보내는 주체이고, 서버는 요청을 처리하고 응답을 반환하는 주체입니다. 서버는 기본적으로 수동적인 존재이며, 요청이 오기 전까지는 아무런 동작을 하지 않습니다.

2. 요청-응답 기반 통신: HTTP의 모든 통신은 요청과 응답의 쌍으로 이루어집니다. 요청이 없으면 응답도 존재하지 않습니다.
이 구조 때문에 서버가 먼저 클라이언트에게 메시지를 보내는 방식은 HTTP만으로는 구현할 수 없습니다.

3. 무상태성(Stateless): HTTP는 무상태(Stateless) 프로토콜입니다. 이는 서버가 이전 요청의 상태를 기억하지 않는다는 의미입니다. 각 요청은 서로 독립적으로 처리되며, 이전 요청과의 관계를 서버가 자동으로 유지하지 않습니다. 그래서 사용자 상태를 유지하기 위해서는 쿠키, 세션, 토큰과 같은 별도의 메커니즘이 필요합니다.

2. HTTP와 TCP의 관계

TCP가 "데이터를 안전하게 실어 나르는 물리적인 통로"를 만든다면,HTTP는 그 통로 위에서 "어떤 요청을 하고, 어떤 식으로 응답하며, 어떻게 인증하고 정보를 캐싱할지" 결정하는 서비스의 논리적 체계입니다.

 

HTTP는 애플리케이션 계층(Application Layer) 프로토콜로, 웹 브라우저와 서버가 어떤 형식으로 데이터를 주고받을지를 정의합니다. 하지만 HTTP 자체는 데이터를 실제로 네트워크를 통해 전송하는 기능은 가지고 있지 않습니다. 실제 데이터 전달은 전송 계층(Transport Layer) 에 위치한 TCP(Transmission Control Protocol) 가 담당합니다.

2.1 TCP

OSI model

TCP는 서버와 클라이언트가 데이터를 신뢰성 있는 전송을 보장하기 위해 만들어진 프로토콜입니다. 데이터를 작은 조각(패킷)으로 나누고, 손실되거나 순서가 뒤바뀐 패킷을 재전송하여 데이터의 무결성과 신뢰성을 보장하는 연결 지향형 방식입니다. 보통 IP(인터넷 프로토콜)와 함께 TCP/IP로 불리며 인터넷 통신의 핵심 기술입니다.

 

TCP는 전이중(Full-Duplex) 방식의 양방향 통신을 지원하여, 연결된 두 프로세스가 동시에 데이터를 송수신할 수 있습니다. 예를 들어 전화 통화처럼, 데이터를 보내면서 동시에 다른 데이터를 받을 수 있다는 뜻이며, 연결 지향적이고 신뢰성이 높아 파일 전송이나 웹 통신(HTTP, HTTPS) 등에 주로 활용됩니다.

  • 연결 지향: 데이터를 보내기 전에 송신자와 수신자 간에 연결을 설정(핸드셰이크)하고, 통신 후 연결을 종료하는 과정을 거칩니다.
  • 신뢰성: 데이터가 정확하게 전달되었는지 확인하고, 손실되거나 잘못된 패킷은 재전송하여 데이터의 완전성을 보장합니다.
  • 순서 보장: 데이터를 보내는 순서와 받는 순서가 동일하게 유지되도록 패킷에 번호를 매겨 재조립합니다.
  • 흐름 제어 및 혼잡 제어: 데이터 전송 속도를 조절하여 네트워크 혼잡을 방지하고 수신자가 데이터를 처리할 수 있는 속도에 맞춰 전송합니다.

TCP 세그먼트

TCP에서 실제로 네트워크를 통해 전송되는 단위는 패킷(packet)이 아닙니다. 정확히 TCP 세그먼트(TCP Segment) 입니다.
TCP의 신뢰성, 순서 보장, 흐름 제어, 혼잡 제어는 모두 이 TCP 헤더에 정의된 필드들을 통해 구현됩니다.

  • 애플리케이션 데이터TCP 계층에서 제어 정보(TCP 헤더) 와 결합하여IP 계층으로 전달되는 전송 단위
  • TCP 세그먼트 = TCP 헤더 + 데이터(Data)
    0                   1                   2                   3
    0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |          Source Port          |       Destination Port        |
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |                        Sequence Number                        |
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |                    Acknowledgment Number                      |
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |  Data |       |C|E|U|A|P|R|S|F|                               |
   | Offset| Rsrvd |W|C|R|C|S|S|Y|I|            Window             |
   |       |       |R|E|G|K|H|T|N|N|                               |
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |           Checksum            |         Urgent Pointer        |
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |                           [Options]                           |
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |                                                               :
   :                             Data                              :
   :                                                               |
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
  • Source Port, Destination Port
    • Source Port: 송신 애플리케이션을 식별하는 포트 번호
    • Destination Port: 수신 애플리케이션을 식별하는 포트 번호
  • TCP는 IP 주소만으로는 어떤 프로세스와 통신하는지 알 수 없기 때문에, 포트를 사용해 프로세스 단위의 통신을 가능하게 합니다.
  • Sequence Number
    • TCP 세그먼트에 포함된 데이터의 첫 번째 바이트 번호
    • 바이트 단위 번호 체계
    • 연결 수립 시 ISN(Initial Sequence Number) 부터 시작
    • Sequence Number는 세그먼트 번호가 아니라 데이터 바이트 기준 번호
    Sequence Number = 1000이고, 데이터 길이 = 500 bytes인 경우에 다음 Sequence Number = 1500입니다.
    FIN, SYN 또한 시퀀스 번호 공간을 1 소비합니다.
  • Acknowledgment Number
    • 다음으로 수신하고자 하는 바이트의 Sequence Number
    • 누적 확인(Acknowledgment) 방식. ACK = “이 번호 이전까지의 데이터는 모두 정상 수신됨”
    • ACK 덕분에 데이터 유실 감지, 재전송 판단, 순서 복구를 효율적으로 수행
  • Data Offset (Header Length)
    • TCP 헤더의 길이를 나타냄
    • 단위: 4바이트(32비트 워드)
    • TCP 옵션이 존재하는 경우에 데이터가 시작되는 정확한 위치를 식별
  • Control Flags (제어 비트)
    • TCP의 상태 변화와 제어는 이 플래그 조합으로 이루어집니다.
      • SYN: 연결 수립 요청
      • ACK: 확인 응답
      • FIN: 송신 방향 종료
      • RST: 비정상 연결 종료
      • PSH: 즉시 애플리케이션 전달
      • URG: Urgent Pointer 유효
      TCP 상태 머신은 이 플래그 조합을 기반으로 동작합니다.
  • Window Size (흐름 제어)
    • 수신 측이 현재 수신 가능한 데이터 크기
    • 단위: 바이트
    • 윈도우 사이즈를 통해 TCP는 송신 속도를 조절하고, 수신 버퍼 오버플로를 방지합니다.
  • Checksum
    • TCP 헤더 + 데이터에 대한 무결성 검사
    • 오류 발생 시 세그먼트 폐기
    • TCP는 전송 계층 수준에서 데이터 무결성까지 보장합니다.

TCP 주요 상태 정리

  • LISTEN: 서버가 연결 요청을 기다리는 상태
  • SYN_SENT: 클라이언트가 SYN을 보내고 응답을 기다리는 상태
  • SYN_RECEIVED: 서버가 SYN을 받고 SYN-ACK를 보낸 상태
  • ESTABLISHED: 데이터 송수신이 가능한 정상 연결 상태
  • FIN_WAIT_1: FIN 전송 후 ACK 대기
  • FIN_WAIT_2: FIN에 대한 ACK 수신 후, 상대 FIN 대기
  • CLOSE_WAIT: FIN 수신 후, 애플리케이션 종료 대기
  • LAST_ACK: FIN 전송 후 마지막 ACK 대기
  • TIME_WAIT: 연결 완전 종료 전, 지연 패킷 대비 대기
  • CLOSED: 연결 종료 상태

HTTP는 웹 페이지, 이미지 등 데이터를 주고받는 응용 계층 프로토콜로, 그 자체로 신뢰성 있는 전송을 담당하지 않으므로, TCP(전송 계층)의 신뢰성 있는 서비스를 빌려와 HTTP 통신이 끊기거나 데이터가 손실되지 않도록, HTTP 통신이 시작되기 전에는 반드시 TCP 연결이 먼저 수립되어야 합니다. 이 연결을 안전하게 맺기 위해 사용되는 과정이 바로 TCP 3-Way Handshake입니다.

2.2 3-Way Handshake

TCP 연결은 단순히 “연결한다, 끊는다”로 이루어지지 않습니다. TCP는 연결의 시작부터 종료까지 전 과정을 명확한 절차로 관리하며, 이를 통해 신뢰성 있는 통신을 보장합니다. TCP 연결의 생명주기는 다음 세 단계로 나눌 수 있습니다.

  1. 연결 수립(Connection Establishment) — 3-Way Handshake
  2. 연결 유지(Data Transfer) — ESTABLISHED 상태
  3. 연결 종료(Connection Termination) — 4-Way Handshake

TCP 3-Way Handshake 전체 흐름

아래 그림을 기준으로 각 단계를 하나씩 살펴보겠습니다.

  • 화살표 방향: 메시지가 이동하는 방향
  • SYN, SYN-ACK, ACK: TCP 제어 플래그
  • Sequence Number, Acknowledgement Number: 데이터 순서와 확인을 위한 번호
  • Connection Established: 3단계가 모두 완료되었음을 의미

이 그림은 단순한 절차 설명이 아니라, TCP가 어떻게 신뢰성을 확보하는지 시각적으로 보여주는 구조도라고 이해하시면 됩니다.

 

1단계: SYN (연결 요청)

첫 번째 단계는 클라이언트가 서버에 연결을 요청하는 단계입니다.
클라이언트는 서버에게 다음과 같은 메시지를 보냅니다.

  • SYN 플래그: 연결을 요청
  • Sequence Number(ISN): 클라이언트가 선택한 초기 시퀀스 번호
### 1단계: SYN
Source IP: Client's IP
Source Port: Client's Port
Destination IP: Server's IP
Destination Port: Server's Port
Flags: SYN
Sequence Number: 1000

예를 들어, 클라이언트가 선택한 초기 시퀀스 번호(ISN)가 1000이라고 가정해 보겠습니다.
이 단계는 사람 사이의 악수에 비유하면, “먼저 손을 내밀어 인사를 청하는 단계”라고 이해하시면 됩니다.

시퀸스 번호란

TCP(Transmission Control Protocol)에서 시퀀스 번호(ISN, Initial Sequence Number)는 연결을 설정할 때 사용되는 초기 순서 번호입니다. 각 데이터 바이트에 고유한 번호를 부여하여 데이터의 순서를 추적하고 신뢰할 수 있는 데이터 전송을 보장하는 데 핵심적인 역할을 합니다. 32비트 값으로, 연결 수명 동안 전송되는 데이터 패킷의 추적 및 순서 지정을 위한 시작점 역할을 합니다. 시퀸스 번호의 특징은 신뢰성 보장, 보안 강화가 목적입니다.

  1. 신뢰성 보장: 데이터 패킷이 네트워크를 통해 전송될 때 손실되거나 순서가 뒤바뀌는 경우, 수신자는 시퀀스 번호를 이용해 패킷을 올바른 순서로 재조립하고 누락된 데이터를 식별할 수 있습니다.
  2. 보안 강화: ISN은 예측 불가능한 난수(랜덤 값)로 생성됩니다. 이는 공격자가 다음 시퀀스 번호를 예측하여 악의적인 패킷을 삽입하거나 기존 연결을 가로채는 '세션 하이재킹' 공격을 방지하는 보안 메커니즘으로 작동합니다.

시퀸스 번호의 동작 방식은 TCP 3-way 핸드셰이크(연결 설정 과정) 중에 클라이언트와 서버는 서로에게 자신의 ISN을 포함한 SYN(Synchronize) 패킷을 보냅니다. 이를 통해 양 끝단은 앞으로 사용할 시퀀스 번호(1000)의 시작점을 동기화합니다. 결론적으로, ISN은 TCP 통신의 신뢰성과 보안을 위한 중요한 기반이며, 데이터가 정확하고 안전하게 전달되도록 보장하는 역할을 합니다.

 

2단계: SYN-ACK (연결 수락 + 확인)

두 번째 단계에서는 서버가 클라이언트의 요청을 받고, 응답합니다.
서버는 다음 정보를 포함한 메시지를 클라이언트에게 전송합니다.

  • SYN 플래그: 연결 수락
  • ACK 플래그: 요청 확인
  • Acknowledgement Number: 클라이언트 ISN + 1 (예: 1001)
  • 서버의 ISN: 서버가 선택한 초기 시퀀스 번호 (예: 5000)
### 2단계: SYN-ACK 
Source IP: Server's IP
Source Port: Server's Port
Destination IP: Client's IP
Destination Port: Client's Port
Flags: SYN, ACK
Sequence Number: 5000
Ackonwlegment Number: 1001
(Client ISN + 1)

여기서 중요한 포인트는 ACK 번호입니다. ACK 번호가 1001이라는 것은, 서버가 “클라이언트의 시퀀스 번호 1000을 정확히 받았다”는 것을 의미합니다. SYN-ACK 단계는 악수 비유로 보면, “상대방이 손을 잡고, 나도 손을 내미는 단계”입니다.

 

3단계: ACK (최종 확인)

마지막 단계에서는 클라이언트가 서버의 응답을 확인합니다. 클라이언트는 다음 정보를 담은 메시지를 서버로 보냅니다.

  • ACK 플래그
  • Acknowledgement Number: 서버 ISN + 1 (예: 5001)
### 3단계: ACK
Source IP: Client's IP
Source Port: Client's Port
Destination IP: Server's IP
Destination Port: Server's Port
Flags: ACK
Sequence Number: 5001
(Server ISN + 1)

ACK 메시지를 받은 서버는 “클라이언트도 내 응답을 정상적으로 받았구나” 라고 판단하게 됩니다. 모든 단계가 끝나면 클라이언트와 서버 모두 서로가 통신 준비가 되었음을 확신하게 되며, TCP 연결 상태는 ESTABLISHED가 됩니다. TCP 연결 상태는 아래에 정리하겠습니다.

TCP 3-Way Handshake가 완료되면, 그제서야 클라이언트는 HTTP 요청(Request)을 서버로 보낼 수 있습니다. 이때 중요한 사실은, HTTP는 TCP 연결 위에서 요청과 응답을 한 번 주고받고 나면 연결을 종료하는 구조로 설계되었습니다. 물론, HTTP/1.1 이후에는 keep-alive 옵션을 통해 TCP 연결을 재사용할 수 있게 되었지만, 통신 모델 자체는 여전히 요청 → 응답 중심입니다. 이 구조 때문에 서버는 TCP 연결이 존재하더라도 클라이언트의 요청 없이 먼저 데이터를 보낼 수는 없습니다.

2.3 4-Way Handshake

TCP 연결 종료는 단순히 “연결을 끊는다”로 끝나지 않습니다. TCP는 전이중(Full-Duplex) 방식의 양방향 통신 프로토콜이기 때문에, 한쪽의 송신 종료와 반대쪽의 송신 종료를 각각 독립적으로 처리해야 합니다. 그래서 연결 종료에는 총 4단계의 메시지 교환, 즉 4-Way Handshake가 필요합니다. TCP 연결 종료의 핵심 포인트를 정리하자면, “나는 이제 더 이상 보낼 데이터가 없어”라는 의사 표현을 서로 주고받는 과정입니다.

TCP 연결 종료는 보통 클라이언트가 먼저 종료를 요청하는 경우가 많지만, 어느 쪽이든 먼저 종료를 원한 쪽이 FIN을 전송합니다.
아래에서는 클라이언트가 먼저 연결 종료를 요청하는 경우를 기준으로 설명하겠습니다.

1단계: FIN (연결 종료 요청)

TCP 연결 종료의 첫 단계는 연결을 종료하고자 하는 쪽이 FIN(Finish) 플래그가 설정된 세그먼트를 전송하는 과정입니다.
이 세그먼트는 해당 방향의 데이터 전송이 더 이상 없음을 TCP 계층에 명시적으로 알리는 제어 신호입니다.

FIN 세그먼트는 연결 전체를 즉시 종료하지 않으며, TCP 연결의 송신 방향을 독립적으로 종료하기 위해 사용됩니다.

 

FIN 세그먼트의 역할과 의미

  • 송신 측은 더 이상 새로운 애플리케이션 데이터를 전송하지 않음
  • TCP 연결의 해당 송신 방향을 정상적으로 종료 요청
  • 상대방의 데이터 수신은 계속 가능

TCP는 양방향(full-duplex) 통신을 지원하므로, 한쪽의 송신 종료와 반대쪽의 송신 종료는 각각 별도로 처리됩니다.
이 설계로 인해 TCP 연결 종료는 일반적으로 4-way handshake 구조를 가집니다.

 

FIN 세그먼트 구성 요소

### 1단계: FIN
Source IP: Client's IP
Source Port: Client's Port
Destination IP: Server's IP
Destination Port: Server's Port
Flags: FIN # 송신 방향 종료 요청
Sequence Number: 2000
  • Sequence Number
    • 송신 측이 마지막으로 전송한 데이터의 시퀀스 번호. FIN 플래그 또한 시퀀스 번호 공간을 1만큼 소비
    • 수신자는 ACK = FIN Sequence Number + 1 로 응답

1) FIN 시퀀스 번호 소비

TCP에서 시퀀스 번호(Sequence Number)는 단순히 “패킷의 번호”가 아닙니다. TCP는 데이터를 패킷 단위가 아닌, 바이트 스트림(Byte Stream) 으로 관리합니다. 따라서 시퀀스 번호는 해당 세그먼트가 포함하는 데이터의 “첫 번째 바이트 위치”를 의미합니다. 예를 들어, 시퀀스 번호가 1000이고, 데이터 길이가 500바이트라면 이 세그먼트는 1000 ~ 1499 바이트를 전송합니다. 수신자는 이를 기준으로 다음에 받을 바이트를 계산하고, ACK 번호를 통해 “어디까지 정상적으로 받았는지”를 알려줍니다.

 

2) TCP 시퀀스 공간(Sequence Space)이라는 개념

TCP는 단순히 데이터만 관리하지 않습니다. TCP 내부에는 시퀀스 공간(Sequence Space) 이라는 개념이 존재합니다.
이 시퀀스 공간에는 다음이 모두 포함됩니다.

  1. 실제 데이터 바이트
  2. 연결 상태를 변경하는 제어 이벤트
    • SYN (연결 시작)
    • FIN (연결 종료)

TCP는 “연결의 시작과 끝도 데이터 흐름의 일부로 취급한다.”고 설계되었습니다.
이 설계 철학이 FIN이 시퀀스 번호를 소비하는 이유의 핵심입니다.

 

3) FIN은 데이터가 없는데, 길이 1로 취급하는 이유

FIN 플래그 자체는 데이터(payload)를 포함하지 않습니다. 하지만 TCP는 FIN을 가상의 1바이트(길이 1의 세그먼트)를 차지하는 이벤트로 취급합니다. 이 말의 의미는 FIN은 실제 데이터를 보내지는 않지만, 시퀀스 번호 관점에서는 하나의 바이트처럼 처리된다.라고 말할 수 있습니다.

 

4) FIN 시퀀스 번호 소비 예제

다음 상황을 가정해 보겠습니다.

  • 클라이언트가 마지막 데이터 바이트를 Sequence Number = 1999까지 전송
  • 이후 더 이상 보낼 데이터가 없음

이때 클라이언트는 FIN을 전송합니다.

FIN
Sequence Number = 2000

FIN이 시퀀스 공간에서 1바이트를 차지하므로, 수신자는 이를 정상적으로 받으면 다음과 같이 응답합니다.

ACK = 2001

이 ACK의 의미는 “네가 보낸 데이터와 종료 이벤트(FIN)까지 모두 정상적으로 처리했다.” 매우 명확합니다.

 

5) FIN을 시퀀스 번호에 포함시키는 이유

  1. 데이터와 종료를 동일한 신뢰성 메커니즘으로 처리를 보장
    1. 시퀀스 번호
    2. ACK
    3. 재전송
    4. 순서 보장
    FIN이 시퀀스 공간에 포함된 덕분에 아래 기능을 보장합니다.
    1. FIN이 유실되면 재전송 가능
    2. FIN 중복 수신도 시퀀스 번호로 식별 가능
    3. 종료 이벤트도 정확히 한 번만 처리됨이 보장됨
  2. TCP 신뢰성 모델을 유지하기 위한 필수 조건입니다. TCP의 핵심 신뢰성 기능은 다음 요소로 구성됩니다.
  3. 마지막 데이터와 연결 종료의 경계를 명확히 구분 가능
  4. 만약 FIN이 시퀀스 번호를 소비하지 않는다면, 마지막 데이터가 어디까지인지 불명확해지고 데이터 수신 완료와 연결 종료 상태가 애매해질 수 있습니다. FIN을 시퀀스 공간에 포함시키면 “이 바이트 다음이 스트림의 끝”이라는 기준이 명확해집니다. 수신자는 안전하게 스트림 종료를 판단할 수 있습니다.
  5. Half-Close 구조를 안전하게 지원
  6. TCP는 송신과 수신 방향이 독립적으로 제어되는 전이중(Full-Duplex) 방식의 양방향 통신 구조를 가집니다. 한쪽에서 FIN 세그먼트를 보낸다는 것은 자신의 송신 통로를 닫겠다는 선언일 뿐, 상대방이 보내는 데이터를 수신하는 귀까지 닫겠다는 의미는 아닙니다. 이처럼 '나는 할 말을 마쳤지만, 상대의 말은 끝까지 듣는' Half-Close 상태는 데이터의 유실 없는 안전한 종료를 보장합니다. 특히 FIN 플래그가 독립적인 시퀀스 번호를 점유하도록 설계된 덕분에, 제어 신호조차 데이터와 동일한 수준의 엄격한 흐름 제어 속에서 오차 없이 상태 전이를 완료하게 됩니다. 만약 FIN이 번호를 소비하지 않는다면, 마지막 데이터에 대한 ACK와 FIN에 대한 ACK를 구분할 수 없게 되어 네트워크 혼란이 생길 수 있습니다.

2단계: ACK (FIN 수신 확인)

이제 수신 측은 FIN 세그먼트를 수신한 뒤 해당 FIN에 대한 확인 응답(ACK) 을 전송합니다.

### 2단계: ACK
Source IP: Server's IP
Source Port: Server's Port
Destination IP: Client's IP
Destination Port: Client's Port
Flags: ACK
Acknowledgement Number: 2001
  • ACK 번호는 FIN 시퀀스 번호 + 1
  • FIN 수신이 정상적으로 처리되었음을 의미

이 단계 이후의 상태 변화는 다음과 같습니다.

  • 송신 측(클라이언트): FIN_WAIT_2
  • 수신 측(서버): CLOSE_WAIT

3단계: FIN (수신 측의 송신 종료 요청)

수신 측이 모든 데이터 전송을 완료하면 자신의 송신 종료를 위해 FIN 세그먼트를 전송합니다.

### 3단계: FIN
Source IP: Server's IP
Source Port: Server's Port
Destination IP: Client's IP
Destination Port: Client's Port
Flags: FIN
Sequence Number: 3000
  • 서버의 송신 방향 종료 요청
  • 상태: LAST_ACK

4단계: ACK (최종 확인)

마지막으로 상대방은 FIN에 대한 ACK를 전송합니다.

### 4단계: ACK
Source IP: Client's IP
Source Port: Client's Port
Destination IP: Server's IP
Destination Port: Server's Port
Flags: ACK
Acknowledgement Number: 3001
  • ACK 수신 후 서버는 CLOSED
  • 클라이언트는 TIME_WAIT 상태로 전이

4-Way HandShake 핵심 정리

  • TCP 연결 종료는 양방향을 독립적으로 종료
  • FIN은 제어 플래그지만 시퀀스 번호를 소모
  • 4-way handshake는 신뢰성 있는 종료를 위한 필수 절차

지금까지 HTTP와 TCP의 관계로 TCP는 왜 신뢰성이 있고, 왜 HTTP가 그 위에서 동작할 수밖에 없는지 공부했습니다. HTTP는 “무엇을 주고받을지”를 정의하고, TCP는 “그 데이터를 어떻게 안전하게 전달할지”를 책임지는 구조를 TCP 위주로 살펴봤으니, 애플리케이션 계층의 HTTP 프로토콜에 대해 공부하겠습니다.

3. 부록

3.1 HTTP 요청(Request) 헤더 전체 정리 표

일반(General) 요청 헤더

헤더 이름 설명 예시
Cache-Control 캐시 동작 방식 제어 no-cache, max-age=3600
Connection 연결 관리 방식 keep-alive, close
Date 요청이 생성된 날짜와 시간 Tue, 14 Jan 2026 03:20:00 GMT
Pragma HTTP/1.0 호환 캐시 제어 no-cache
Via 프록시를 거쳐온 경로 정보 1.1 proxy.example.com

 

요청 대상(Request Target) 관련 헤더

헤더 이름 설명 예시
Host 요청 대상 서버의 도메인과 포트 (필수) example.com:8080
  • HTTP/1.1에서는 Host 헤더가 필수: 하나의 IP에서 여러 도메인을 처리하기 위해 필요합니다.

 

클라이언트 정보(User Agent) 헤더

헤더 이름 설명 예시
User-Agent 클라이언트 프로그램 정보 Mozilla/5.0 Chrome/120.0
Accept 수신 가능한 미디어 타입 application/json
Accept-Language 선호 언어 ko-KR,ko;q=0.9
Accept-Encoding 허용 압축 방식 gzip, deflate, br
Accept-Charset 허용 문자 인코딩 UTF-8

 

콘텐츠(Content) 관련 헤더

헤더 이름 설명 예시
Content-Type 요청 바디의 데이터 타입 application/json
Content-Length 요청 바디의 바이트 길이 348
Content-Encoding 바디 압축 방식 gzip
Content-Language 바디의 언어 ko-KR
  • Content-Length문자 수가 아니라 바이트 수입니다.

Content-Type과 Content-Length의 의미

  • Content-Type
  • Content-Type은 Body에 담긴 데이터의 형식(MIME 타입)을 명시합니다. 이 헤더가 없다면, 서버는 요청 Body를, 클라이언트는 응답 Body를 어떻게 해석해야 할지 알 수 없습니다. 따라서 Content-Type은 서로 다른 시스템 간 데이터 해석을 가능하게 하는 핵심 약속입니다.
  • Content-Length
  • Content-Length는 Body의 바이트 단위 길이를 나타냅니다. HTTP는 기본적으로 스트림 기반 통신이기 때문에, 수신 측은 “어디까지가 하나의 메시지인지”를 알아야 합니다. Content-Length는 이 경계를 명확히 해주는 역할을 합니다.

인증(Authentication) 관련 헤더

헤더 이름 설명 예시
헤더 이름 설명 예시
Authorization 인증 정보 전달 Bearer eyJhbGciOi...
Proxy-Authorization 프록시 서버 인증 Basic QWxhZGRpbjpvcGVu

 

쿠키(Cookie) 관련 헤더

헤더 이름 설명 예시
Cookie 클라이언트가 저장한 쿠키 전달 SESSIONID=abc123

요청에서는 Cookie, 응답에서는 Set-Cookie

 

조건부 요청(Conditional Request) 헤더

헤더 이름 설명 예시
If-Modified-Since 특정 시점 이후 변경 여부 Tue, 10 Jan 2026 12:00:00 GMT
If-None-Match ETag 비교 "v1.2.3"
If-Match ETag 일치 시에만 처리 "v1.2.3"
If-Unmodified-Since 변경되지 않았을 때만 처리 Mon, 09 Jan 2026 10:00:00 GMT

범위(Range) 요청 헤더

헤더 이름 설명 예시
Range 요청 데이터의 범위 지정 bytes=0-1023
  • 대용량 파일 다운로드, 영상 스트리밍에 사용

보안(Security) 관련 헤더

헤더 이름 설명 예시
Origin 요청 출처 https://example.com
Referer 이전 페이지 주소 https://example.com/page
Upgrade-Insecure-Requests HTTPS 선호 여부 1
Sec-Fetch-Site 요청 출처 관계 same-origin
Sec-Fetch-Mode 요청 모드 cors
Sec-Fetch-Dest 요청 대상 document

 

CORS & COOP 관련 요청 헤더

헤더 이름 설명 예시
Access-Control-Request-Method 실제 요청 메서드 POST
Access-Control-Request-Headers 실제 요청 헤더 Authorization,Content-Type
Cross-Origin-Opener-Policy 팝업 차단 에러를 방지 same-origin-allow-popups
  • Preflight 요청(OPTIONS)에서만 사용

 

대표적인 커스텀(Custom) 헤더

헤더 이름 설명 예시
X-Request-Id 요청 추적 ID req-12345
X-User-Id 사용자 식별 정보 42
X-User-Role 사용자 권한 정보 ADMIN

3.2 4xx 클라이언트 오류 코드 표

코드 이름 의미(요약) 언제 쓰는지(대표 사례)
400 Bad Request 요청 형식/값이 잘못됨 JSON 파싱 실패, 필수 파라미터 누락, 타입 오류, 유효성 검증 실패
401 Unauthorized 인증 실패(로그인 필요/토큰 무효) Access Token 없음/만료/위조, 인증 헤더 누락
402 Payment Required 결제 필요(예약됨) 표준상 예약, 실무에서는 거의 안 씀
403 Forbidden 인가 실패(권한 없음) 로그인은 했지만 접근 권한이 없음(ROLE 부족)
404 Not Found 자원이 없음 존재하지 않는 리소스 ID, 잘못된 경로
405 Method Not Allowed 메서드 불가 POST /users/1만 허용인데 GET 호출
406 Not Acceptable 클라이언트가 원하는 응답 형식 제공 불가 Accept 헤더 요구 형식 미지원
407 Proxy Authentication Required 프록시 인증 필요 기업/프록시 환경에서 프록시 인증 요구
408 Request Timeout 요청 시간 초과 클라이언트가 제시간에 요청을 완성 못함
409 Conflict 자원 상태 충돌 중복 생성(유니크 충돌), 버전 충돌(낙관적 락)
410 Gone 자원이 영구 삭제됨 예전엔 있었지만 이제는 폐기된 리소스
411 Length Required Content-Length 필요 서버가 길이를 요구하는데 누락됨(드묾)
412 Precondition Failed 조건부 요청 실패 If-Match/If-Unmodified-Since 조건 불일치
413 Payload Too Large 요청 본문이 너무 큼 업로드 용량 제한 초과
414 URI Too Long URL이 너무 김 쿼리스트링 과도(검색 조건 과다)
415 Unsupported Media Type 요청 본문 형식 미지원 Content-Type이 서버 미지원(예: JSON만 받는데 XML 전송)
416 Range Not Satisfiable Range 범위가 유효하지 않음 파일 다운로드 범위 요청이 잘못됨
417 Expectation Failed Expect 헤더 요구 실패 Expect: 100-continue 관련(드묾)
418 I’m a teapot 장난/이스터에그 실무에서는 사용하지 않음
421 Misdirected Request 잘못된 대상 서버로 라우팅 HTTP/2 연결 재사용/가상호스트 문제(드묾)
422 Unprocessable Content (Entity) 문법은 맞지만 의미적으로 처리 불가 검증 실패를 400 대신 422로 분리하는 팀도 많음
423 Locked 리소스 잠김 잠금 상태(웹DAV 계열, 드묾)
424 Failed Dependency 선행 요청 실패 의존 작업 실패(웹DAV, 드묾)
425 Too Early 너무 이른 요청 재전송 공격 방지 맥락(드묾)
426 Upgrade Required 프로토콜 업그레이드 필요 HTTP→TLS 등 업그레이드 요구(드묾)
428 Precondition Required 조건부 요청 필요 ETag 기반 조건 요청 강제(동시성 제어용)
429 Too Many Requests 과도한 요청(레이트 리밋) API 호출 제한 초과, 봇/남용 방지
431 Request Header Fields Too Large 헤더가 너무 큼 쿠키/헤더 과도(특히 Cookie 폭증)
451 Unavailable For Legal Reasons 법적 사유로 제공 불가 법적 요청/차단으로 접근 제한

 

5xx 서버 오류 코드 표

코드 이름 의미(요약) 언제 쓰는지(대표 사례)
500 Internal Server Error 서버 내부 예외 미처리 예외, NullPointer, 예상 밖 장애
501 Not Implemented 미구현 기능 서버가 해당 메서드/기능을 구현하지 않음
502 Bad Gateway 게이트웨이가 잘못된 응답 수신 API Gateway/프록시가 백엔드로부터 비정상 응답/연결 실패
503 Service Unavailable 서비스 이용 불가(일시적) 점검/과부하/다운, 오토스케일 중
504 Gateway Timeout 게이트웨이 타임아웃 백엔드가 응답을 늦게 줘서 게이트웨이가 타임아웃
505 HTTP Version Not Supported HTTP 버전 미지원 서버가 해당 HTTP 버전을 지원하지 않음
506 Variant Also Negotiates 협상 설정 문제 콘텐츠 협상 관련(거의 안 씀)
507 Insufficient Storage 저장소 부족 서버 스토리지 부족(WebDAV 계열)
508 Loop Detected 루프 감지 리다이렉션/처리 루프(WebDAV)
510 Not Extended 확장 요구 미충족 확장 프레임워크용(거의 안 씀)
511 Network Authentication Required 네트워크 인증 필요 캡티브 포털 등 네트워크 인증 환경(드묾)

 

실무에서 특히 “설계 수준”을 가르는 포인트

  • 401 vs 403
    • 401: “인증이 안 됨(토큰 문제/로그인 필요)”
    • 403: “인증은 됐는데 권한이 없음(ROLE 부족)”
  • 400 vs 422
    • 팀/서비스 정책에 따라 다릅니다.
    • 보통은 검증 실패를 400으로 통일해도 충분하고, 더 엄격히 나누고 싶으면 422를 씁니다.
  • 500 vs 502/504
    • 500: 애플리케이션 자체 예외
    • 502/504: “게이트웨이/프록시 관점에서” 백엔드 연결·응답 문제 (MSA에서 중요)
  • 429는 요즘 거의 필수
    • 레이트 리밋/남용 방지/서버 보호의 대표 코드입니다.

참고 자료