HTTP Header와 Restful API의 성숙도
HTTP Header
HTTP 헤더는 클라이언트와 서버가 요청 또는 응답 시 부가적인 정보를 함께 전송할 수 있도록 하는 메커니즘이다. 이러한 부가 정보에는 메시지 본문(Message Body)의 내용, 크기, 인증 정보, 클라이언트 및 서버에 대한 정보 등이 포함된다.
Header 구조
field-name: OWS field-value OWS
- field-name: 헤더의 이름을 나타낸다 (대소문자 구분 없음).
- field-value: 헤더의 값이다.
- OWS: Optional Whitespace (띄어쓰기 허용).
- 각 헤더는 하나의 줄로 구분된다.
- HTTP 전송에 필요한 다양한 부가 정보를 표현할 수 있으며, 서버와 클라이언트 모두 이해할 수 있다면 임의의 헤더를 추가할 수도 있다.
- 텍스트 형식으로 이루어져 있다.
실제 HTTP Header 확인 방법
웹 브라우저의 개발자 도구(F12) → Network 탭 → Fetch/XHR 탭을 클릭하고, 특정 요청을 선택하면 우측의 Headers 정보에서 실제 HTTP 헤더 내용을 확인할 수 있다.
HTTP Header 모음집
HTTP 헤더는 기능에 따라 여러 종류로 나눌 수 있다.
1. 표현 헤더 (Representation Headers)
실제 데이터를 전송할 때 데이터의 형식이나 압축 방식 등 리소스에 대한 표현 정보를 나타낸다. 요청과 응답 모두에 사용된다.
- Content-Type: 전송하는 데이터의 미디어 타입 및 문자 인코딩을 명시한다.
- 예: text/html; charset=utf-8, application/json
- Content-Encoding: 데이터 압축 방식을 명시한다. 데이터를 압축하여 전송하고, 받는 쪽에서 이 정보를 바탕으로 압축을 해제한다.
- 예: gzip, identity (압축 없음)
- Content-Language: 데이터의 언어를 표현한다.
- 예: ko, en
- Content-Length: 페이로드 (Payload)의 크기를 바이트 단위로 나타낸다.
2. 컨텐츠 협상 헤더 (Content Negotiation Headers)
클라이언트가 서버에게 선호하는 표현 방식을 요청할 때 사용되는 헤더이다 (요청 시에만 사용). 우선순위를 나타내는 Quality Values (q 값, 0~1)을 사용하여 선호도를 표현하며, 값이 1에 가까울수록 우선순위가 높다 (생략 시 1로 간주).
- Accept: 선호하는 미디어 타입을 지정한다.
- 예: Accept: application/json, text/plain, */* (JSON을 가장 선호, 다음은 텍스트, 나머지는 모두 허용)
- Accept-Charset: 선호하는 문자 인코딩을 지정한다.
- Accept-Encoding: 선호하는 압축 인코딩을 지정한다.
- Accept-Language: 선호하는 언어를 지정한다.
- 예: Accept-Language: ko-KR,en-US;q=0.9,en;q=0.8 (한국어(한국) 우선, 다음은 영어(미국), 다음은 영어)
- ex1) Accpet: text/*, text/plain, text/plain;format=flowed, */*
- 구체적으로 선언된 것이 우선순위가 높다.→ text/plain;format=flowed ⇒ text/plain ⇒ text/* ⇒ */*
3. 일반 정보 헤더 (General Headers)
요청이나 응답에 대한 일반적인 정보를 제공하는 헤더이다.
- From: 클라이언트의 이메일 주소 (잘 사용되지 않음).
- Referer: 현재 요청된 페이지의 이전 웹 페이지 주소 (유입 경로 파악에 사용).
- User-Agent: 클라이언트 애플리케이션 정보 (브라우저 종류, 운영체제 등). 접속 환경 통계 및 특정 환경에서의 문제 파악에 활용된다 (요청 시 사용).
- Server: 요청을 처리하는 Origin 서버의 소프트웨어 정보 (응답 시 사용).
- Date: HTTP 메시지가 발생한 날짜와 시간 (응답 시 사용).
4. 특별 정보 헤더 (Special Headers)
특정 상황에서 중요한 정보를 전달하는 헤더이다.
- Host: 요청한 도메인 정보 (필수적으로 포함해야 하는 요청 헤더).
- Host 헤더는 HTTP/1.1부터 필수 헤더이며, 가상 호스팅 환경에서 어떤 도메인으로 요청이 왔는지 서버가 식별하는 데 매우 중요한 역할
- Location:
- 응답 코드 3xx와 함께 사용되면 리다이렉트 주소를 나타낸다.
- 응답 코드 201 Created와 함께 사용되면 새로 생성된 리소스의 URI를 나타낸다.
- Allow: 서버가 허용하는 HTTP 메소드를 나열한다 (응답 코드 405 Method Not Allowed와 함께 사용).
- 예: Allow: GET, POST
- Retry-After: 서버가 일시적으로 요청을 처리할 수 없을 때 (응답 코드 503 Service Unavailable), 다음 요청까지 대기해야 하는 시간을 초 또는 날짜 형식으로 알려준다.
5. 인증 헤더 (Authentication Headers)
클라이언트와 서버 간의 인증 정보를 교환하는 데 사용되는 헤더이다.
- Authorization: 클라이언트가 서버에 인증 정보를 제공할 때 사용한다 (선택한 인증 방식에 따라 값이 달라짐).
- WWW-Authenticate: 서버가 클라이언트에게 필요한 인증 방법을 알려줄 때 사용한다 (응답 코드 401 Unauthorized와 함께 사용).
6. Cookie 헤더
HTTP는 Stateless 특성을 가지므로, 클라이언트와 서버 간에 상태를 유지하기 위해 Cookie를 사용한다. Cookie 헤더를 통해 매 요청마다 상태 정보가 전달된다. 사용자 세션 관리, 광고 정보 추적 등에 주로 사용된다.
- Set-Cookie: 서버가 응답 시 클라이언트에게 Cookie 값을 전달하며, 만료 기간 (expire, max-age), 사용될 위치 (domain, path), 보안 설정 (Secure, HttpOnly, SameSite) 등을 설정할 수 있다.
- 주의: Cookie는 매 요청마다 서버에 전달되므로 최소한의 정보만 사용하여 트래픽을 최적화해야 하며, 탈취 위험이 있으므로 민감한 개인 정보는 저장하지 않아야 한다.
- Secure: HTTPS 연결에서만 쿠키를 전송하도록 설정한다 (기본적으로 HTTP, HTTPS 모두에서 전송).
- 중간자 공격 방지
- HttpOnly: HTTP 전송에만 사용하도록 설정하여 JavaScript에서의 쿠키 접근을 막아 보안을 강화한다.
- XSS 공격 방지
- SameSite: 쿠키가 설정된 도메인과 동일한 도메인에서만 쿠키를 전송하도록 제한하여 CSRF 공격을 방지한다.
- CSRF 공격 방지
- Cookie: 클라이언트가 서버로부터 받은 쿠키를 서버에 다시 전송할 때 사용한다.
7. 캐시 헤더 (Cache Headers)
캐시를 통해 동일한 요청에 대한 응답 데이터를 재사용하여 성능을 향상시키고 불필요한 데이터 전송을 줄인다.
- Cache-Control: 응답 시 사용하는 헤더로, 캐시 동작 방식을 제어한다.
- max-age=<seconds>: 캐시 유효 시간 (초). 이 시간이 지나면 서버에 재검증해야 한다.
- no-cache: 캐시된 데이터를 사용하기 전에 항상 서버에 검증해야 한다.
- no-store: 캐시를 전혀 하지 않는다 (보안에 민감한 데이터에 사용).
- If-Modified-Since: 클라이언트가 캐시한 데이터의 최종 수정일을 서버에 보내, 해당 시점 이후에 데이터가 수정되었는지 확인하는 요청 헤더이다.
- Last-Modified: 서버가 응답 시 데이터의 최종 수정 시간을 알려주는 헤더이다. If-Modified-Since 요청에 대한 응답으로 사용된다.
- 304 Not Modified 상태 코드와 함께 응답되면 데이터가 수정되지 않았음을 의미하며, 클라이언트는 캐시된 데이터를 사용한다 (응답 본문 없음).
- ETag: 데이터의 내용에 기반한 식별자 (이름)를 캐시에 사용한다. If-Modified-Since와 Last-Modified 방식의 한계 (수정 시간은 같지만 내용이 다를 경우, 불필요한 재전송 등)를 해결한다. 요청 시 If-None-Match 헤더와 함께 사용된다.
RESTful API
REST (Representational State Transfer) 원칙을 잘 준수하는 API를 RESTful API라고 한다. HTTP 프로토콜을 사용하여 클라이언트와 서버 간 통신하며, 자원 (Resource)은 고유한 URI로 식별되고, HTTP 메소드를 통해 자원에 대한 CRUD (Create, Read, Update, Delete) 연산을 수행한다. 요청과 응답은 주로 JSON 또는 XML 형식으로 이루어진다.
REST (Representational State Transfer)란?
자원 (Resource)을 이름 (Name)으로 구분하고, 해당 자원의 상태 (정보)를 주고받는 아키텍처 스타일이다. URI를 통해 자원을 명시하고, HTTP Method를 통해 해당 자원에 대한 CRUD Operation을 적용하는 것을 REST라고 한다.
RESTful API 설계 시 참고 사항
- 명사 형태의 리소스 사용: URI는 자원을 나타내는 명사를 사용해야 한다 (단수보다는 복수 형태 권장).
- 동사 대신 HTTP Method 활용: CRUD 함수명 대신 HTTP Method (POST, GET, PUT, DELETE, PATCH 등)를 사용하여 자원에 대한 행위를 정의한다.
- 자원의 계층 관계 표현: 슬래시 (/)를 사용하여 자원의 계층 관계를 나타낸다.
- URI 마지막 문자 슬래시 금지.
- 하이픈 (-) 사용, 언더바 (_) 지양.
- 소문자 사용.
- URI에 파일 확장자 포함 금지.
- 정렬, 필터링, 페이징은 Query Parameter 활용.
- 예외 처리를 위한 일관된 접근 방식 정의.
Maturity Model (성숙도 모델)
REST API가 REST의 제약 조건을 얼마나 잘 준수하는지에 따라 레벨을 구분하는 방법이다.
- Level 0: 단순히 웹 서비스를 제공하기 위해 URL만 매핑한 상태 (모든 요청이 단일 URI로 전송되는 경향).
- 단순히 URL 매핑만 되어 있다는 설명 외에, HTTP Method를 일관성 없이 사용하거나 (대부분 POST 사용 등)
- URI가 리소스를 제대로 나타내지 못하는 것
POST /operation
{
"operation": "createUser",
"data": {
"name": "sparta",
"password": "codingclub"
}
}
- Level 1: 리소스별로 의미 있는 URL을 사용하지만, HTTP Method를 제대로 활용하지 않는 단계 (대부분 POST 또는 GET으로 처리).
- Level 1이 리소스별 엔드포인트를 가지지만, HTTP Method를 의미론적으로 활용하지 못하고 주로 GET과 POST만 사용하는 한계를 가지고 있다
POST /users
{
"name": "sparta",
"password": "codingclub"
}
- Level 2: 리소스의 용도와 상태에 따라 적절한 HTTP Method (GET, POST, PUT, PATCH, DELETE)를 사용하여 API를 설계하는 단계 (CRUD와 HTTP Method를 매칭).
GET /users/123 // 특정 사용자 조회
POST /users // 사용자 생성
{
"name": "sparta",
"password": "codingclub"
}
PUT /users/123 // 사용자 정보 수정
{
"name": "java",
"password": "spring"
}
DELETE /users/123 // 사용자 삭제
- Level 3 (HATEOAS): 응답 내에 다음 가능한 액션 (링크) 정보를 포함하여 클라이언트가 서버의 상태 변화에 따라 동적으로 탐색할 수 있도록 하는 단계.
- HATEOAS가 RESTful API의 핵심 원칙 중 하나이며, 클라이언트가 서버의 응답만으로 다음 상태 전이를 파악할 수 있게 하여 API의 독립성과 진화 가능성을 높인다
GET /users/123
{
"id": 123,
"name": "sparta",
"links": {
"self": "/users/123",
"update": "/users/123",
"delete": "/users/123"
}
}
RESTful API 설계 시 고려 사항
- Consumer first: API 소비자 (개발자 등) 입장에서 간단하고 직관적인 API 설계.
- Make best use of HTTP: HTTP Method, Request/Response, Header 등 HTTP의 장점을 최대한 활용. 최소 Level 2 준수 권장.
- Response Status: 각 API 요청에 대해 적절한 HTTP 상태 코드를 반환하고, 성공/실패 이유를 명확히 전달.
- No secure info in URI: URI에 사용자 정보 등 보안에 민감한 정보 포함 금지.
- Use plurals: 리소스는 복수 형태로 표현 (/user → /users). 특정 리소스는 엔드포인트에 ID를 추가 (/users/1).
- User nouns for resources: 리소스는 동사 대신 명사 형태로 표현하여 API URI만으로도 의미를 파악할 수 있도록 설계.
- For exceptions - define a consistent approach: 예외 처리 방식에 대한 일관성 유지.