TEXT, IMAGE, FILE, HTML, JSON 등 다양한 형태의 데이터가 HTTP를 통해 전송됨. HTTP에도 버전이 존재하며 대부분 HTTP/1.1 (TCP)을 사용하지만, 최근에는 HTTP/2, HTTP/3 (UDP)의 사용량이 급속도로 증가하는 추세.
- HTTP 동작 순서
- 클라이언트는 Request(요청)을 보내고 응답을 기다림
- 서버는 요청에 대한 처리를 수행한 후, 결과를 Response(응답)함
HTTP는 인터넷 상에서 불특정 다수의 통신 환경을 기반으로 설계되었기 때문에 서버에서 다수의 클라이언트와 상태나 연결을 유지해야 한다면, 많은 리소스가 필요함
- 클라이언트와 서버 구조
- 클라이언트는 UI(User Interface)에 중점
- 서버는 데이터, 비즈니스 로직에 중점
- 무상태 (Stateless)
- 비연결 (Connectionless)
HTTP Message는 요청 / 응답 메시지 두 가지 종류가 있으며, 각각 구조가 다름
- HTTP 요청 메시지(Request Message)
- Start Line
- HTTP Method
- 요청의 의도를 가진 GET, POST, PUT, PATCH, DELETE 등이 존재
- Create - POST
- Read - GET
- Update - PUT(전체), PATCH(일부)
- Delete - DELETE
- Request Target
- path
- HTTP Request가 전송되는 대상, 절대 경로(”/”로 시작하는 경로)
- Query String(= Query Parameter) 에 해당하는 값도 포함
- HTTP Version
- Header
field-name: OWS field-value OWS (OWS : 띄어쓰기 허용)
field-name
은 대소문자 구분을 하지 않는다.- 임의의 Header 추가 가능 (단, 서버가 값을 알고 있어야 함)
- 요청의 추가 정보들(Message Body 내용, 크기, 인증, ...)을 가지고 있음
- Empty Line
- 공백 한 줄로서 필수값
- Message Body
- 실제 전송하는 데이터가 담겨 있는 부분
- HTML, 이미지, JSON 등 byte로 표현되는 모든 데이터 전송 가능
- 요청 시 GET의 경우 Message Body가 지원되지 않는 경우가 많아 권장하지 않는다.
- HTTP 응답 메시지(Response Message)
- Start Line
- HTTP Version
- Status Code
- 요청이 성공했는지, 실패했는지 나타내는 코드
- Status Text
- 코드와 함께 전달될 메세지
- Header
- Response에서만 사용되는 Header 값들이 따로 존재
- Empty Line
- 공백 한줄로서 필수값
- Message Body
- 실제 전송하는 데이터가 담겨 있는 부분
- 만약 전송할 데이터가 없다면, Body가 공백으로 존재
- POST
- 리소스 생성
- 주로 HTML FORM(회원가입, 게시글 작성 등)에 사용
- Message Body를 통해 요청 데이터를 전달
- GET
- 리소스 조회
- 서버에 추가적인 데이터 전송을 해야하는 경우, Query String(Query Parameter)를 사용
- GET의 경우, Message Body가 지원되지 않는 경우가 많기 때문에
- PUT
- 리소스 덮어쓰기
- POST와 달리 클라이언트 측에서 리소스를 식별하여 URI 지정
- 기존 리소스 전체 수정 가능
- 기존 리소스의 일부만 수정하더라도 완전히 덮어쓰기가 됨
- 기존 리소스가 없는 경우에는 새로 생성됨
- PATCH
- 리소스 부분 수정
- DELETE
- 리소스 삭제
HTTP Method는 안전성(Safe), 멱등성(Idempotent), 캐시가능성(Cacheable) 속성을 가지고 있음
- Optional은 있을 수도 있다라는 말과 같음
- 안정성(Safe)
- 데이터를 변환하지 않는 것
- GET 메소드(조회)는 안전
- POST, PUT, PATCH, DELETE(생성, 수정, 삭제)는 안전하지 않음
- 멱등성(Idempotent)
- 한 번을 호출하거나 수 천번을 호출하거나 항상 같은 결과를 나타내는 것
- GET은 같은 결과를 계속 조회하기 때문에 멱등성을 보장함
- PUT은 수정한 후의 결과가 계속 같기 때문에 멱등성을 보장함
- DELETE는 같은 요청을 여러 번해도 삭제된 결과가 같기 때문에 멱등성을 보장함
- POST, PATCH는 멱등성을 보장하지 않음💡 멱등성이 필요한 이유
- 요청이 실패한 경우의 재시도 및 복구 메커니즘에 사용하기 위해 필요
- 리소스 조회(GET) 재요청 중간에 리소스가 변경되는 것은 멱등성으로 고려하지 않음 (ex. 실시간 대여 및 판매)
- 캐시 가능성(Cacheable)
- 재사용을 위해 요청에 대한 응답을 저장할 수 있는지 판단
- 일반적으로 GET, HEAD 정도만 캐시로 사용
- 변경 가능성이 적은 정적 자원을 주로 캐싱
💡 캐시(Cache)란?
클라이언크가 서버에 한번 요청했던 데이터에 대해서 매번 요청할 때마다 다시 전송할 필요가 없도록 웹 브라우저가 임시적으로 데이터를 보관하고 있는 장소
HTTP 요청에 대한 처리 상태를 응답하는 코드로서 Data를 함께 응답. Spring에서는 Response를 커스텀하여 의미있는 메세지를 만들어 사용하기도 함.
- 1xx (정보)
- 요청 수신 후 처리 중인 상태
- 잘 사용하지 않음
- 2xx (성공)
- 정상 처리 완료된 상태
- 3xx (리다이렉션)
- 요청을 완료하려면 추가 행동이 필요한 상태
- 3xx 응답 + Location HTTP Header가 있으면 Location 위치로 리다이렉트(이동)함
- 영구 리다이렉션
- 301 : POST 요청이 리다이렉션시 GET 요청으로 변하며, 본문이 제거될 수 있음
- 308 : POST 요청이 리다이렉션시 POST 요청으로 유지되며, 본문 또한 유지됨
- 4xx (클라이언트 에러)
- 클라이언트측 오류, 잘못된 문법 등으로 서버가 요청을 수행할 수 없는 상태
- 클라이언트의 요청이 잘못되었기 때문에 같은 요청의 재시도는 실패함
- 5xx (서버 에러)
- 서버 오류, 요청은 정상이지만 서버가 처리하지 못하는 상태
- 재시도하면 성공할 수 있음
- 발생하지 않도록 하는 것이 최선
🌟🌟 응답 코드를 상황에 맞게 잘 작성하는 것은 매우 기본이며 매우 중요함 🌟🌟
클라이언트와 서버가 요청 또는 응답으로 부가적인 정보(Message Body 내용, 크기, 인증, 브라우저 정보, 서버 정보 등)를 전송할 수 있도록 해줌
- Header 구조
field-name: OWS field-value OWS
(OWS : 띄어쓰기 허용)field-name
은 대소문자 구분을 하지 않음- HTTP 전송에 필요한 모든 부가정보를 표현 가능
- 임의의 Header를 추가할 수 있지만, 서버가 값을 알고있어야 함
- 텍스트 (plain text)로 구성되어 있음
- 각각의 헤더는 하나의 줄로 구분됨
- HTTP Header 확인하는 방법
- 개발자도구(F12) → Network 탭 → Fetch/XHR 탭 → 우측 Header 정보
REST를 잘 준수하는 API로 HTTP 프로토콜을 사용하여 클라이언트와 서버 간의 통신을 통해 자원(Resource)을 관리함. 자원은 고유한 URI로 식별되며, HTTP 메소드(GET, POST, PUT, DELETE 등)를 통해 다양한 작업을 수행하며 요청과 응답은 일반적으로 JSON 또는 XML 형식으로 이루어짐.
- REST(Representational State Transfer)란?
- 자원을 이름(Name)으로 구분하여 해당 자원의 상태(정보)를 주고받는 것
- (= URI를 통해 자원을 명시하고, HTTP 메소드를 통해 해당 자원에 대한 CRUD를 적용하는 것)
- REST API URI 명명 규칙
1. 리소스는 명사를 사용해야 함
- 단수가 아닌 복수 형태를 사용해야 함
- 만약, REST만으로 해결하기 어려운 경우에 한해서 동사를 허용
- 자원의 계층 관계를 슬래시(/)로 표현
- 마지막 문자에는 슬래시(/)가 있으면 안됨
- 언더바(_)가 아닌 하이픈(-)을 사용해야 함
- 소문자를 사용해야 함
- URI에 파일 확장자를 포함하면 안됨
- CRUD 함수명은 사용하지 않고, HTTP 메소드를 활용해야 함
- 정렬, 필터링, 페이징은 신규 API를 만드는것이 아닌 Query Parameter를 사용해야 함
REST의 제약 조건에 따라 API를 등급화하는 방법
- Level0
- 웹 서비스를 제공하기 위해 URL만 매핑해 놓은 상태
- 모든 요청이 단일 URI로 전송됨
POST /operation { "operation": "createUser", "data": { "name": "sparta", "password": "codingclub" } }
- Level1
- 외부로 공개하려는 리소스에 대해서 의미있는 URL로 표현하기 시작한 단계
- 적절한 패턴을 가지고 작성 되었지만 HTTP 메소드 별로 서비스를 구분하지 않음
- 사용자의 요청을 GET, POST로 대부분 처리하고 에러를 반환함
- 리소스에 대해 분리된 엔드포인트를 가짐
POST /users { "name": "sparta", "password": "codingclub" }
- Level2
- 우리가 제공하고자 하는 리소스를 HTTP 메소드에 맞게 설계하고 서비스하는 단계.
- HTTP 메소드를 이용하여 리소스 상태를 구분하고 서비스 하게 되면 비슷한 이름의 URI라 하더라도 다른 형태의 서비스 제공 가능
- HTTP 메소드 활용
GET /users/123 // 특정 사용자 조회 POST /users // 사용자 생성 { "name": "sparta", "password": "codingclub" } PUT /users/123 // 사용자 정보 수정 { "name": "java", "password": "spring" } DELETE /users/123 // 사용자 삭제
- Level3
- 다음 단계에서 어떠한 작업을 할 수 있는지에 대한 상태 정보를 함께 넘겨줌
- 클라이언트 측에서는 서버가 제공하는 서비스를 일일이 찾는 수고를 겪지 않아도 됨
- 응답 내에 링크를 포함
GET /users/123 { "id": 123, "name": "sparta", "links": { "self": "/users/123", "update": "/users/123", "delete": "/users/123" } }
💡 HATEOAS(Hypermedia As The Engine Of Application State)
현재 작업의 다음 단계에서 진행할 수 있는 작업에 대한 정보를 같이 알려주는 기능을 HATEOAS라고 함
- Consumer first
- 개발자가 아닌 해당 API의 소비자의 입장에서 간단하고 직관적인 API를 설계 해야함
- 위에서의 소비자는 엔드유저가 아닌 API를 사용 하고있는 또다른 시스템, 개발자 등을 의미
- Make best use of HTTP
- HTTP의 장점(HTTP 메소드, 요청, 응답, 헤더 등)을 살려서 개발해야 함
- Request methods
- 최소한 성숙도 모델 Level2로는 사용해야 함
- Response Status
- 각각의 API 요청에 따라서 적절한 HTTP 상태 코드가 전달되어야 함
- 단순히 성공, 실패가 아닌 왜 실패하고 성공 하였는지도 함께 반환 시켜야함
- No secure info in URI
- URI에 사용자의 정보를 포함해서는 안됨
- Use plurals
- 제공하는 데이터에 대하여 단수가 아닌 복수형태로 사용해야 함
- /user(x) → /users(o)
- 특정 유저를 찾고자 한다면 엔드포인트에 값을 추가
- /users/1
- User nouns for resources
- 모든 리소스는 가능하면 명사 형태로 표시
- API URI만 보고도 어떠한 API인지 파악할 수 있는 것이 좋음
- For exceptions - define a consistent approach
- 일괄적인 엔드포인트를 사용하는 것이 좋음
✅ 출처
https://en.wikipedia.org/wiki/Hypertext_Transfer_Protocol
https://restfulapi.net/resource-naming/
https://dev.to/ragrag/rest-api-maturity-towards-the-glory-of-rest-5cm3